diff --git a/src/main/java/me/kavin/piped/ServerLauncher.java b/src/main/java/me/kavin/piped/ServerLauncher.java index 71893c6..8a738fc 100644 --- a/src/main/java/me/kavin/piped/ServerLauncher.java +++ b/src/main/java/me/kavin/piped/ServerLauncher.java @@ -82,6 +82,13 @@ public class ServerLauncher extends MultithreadedHttpServerLauncher { } catch (Exception e) { return getErrorResponse(e, request.getPath()); } + })).map(GET, "/clips/:clipId", AsyncServlet.ofBlocking(executor, request -> { + try { + return getJsonResponse(ResponseHelper.resolveClipId(request.getPathParameter("clipId")), + "public, max-age=31536000, immutable"); + } catch (Exception e) { + return getErrorResponse(e, request.getPath()); + } })).map(GET, "/channel/:channelId", AsyncServlet.ofBlocking(executor, request -> { try { return getJsonResponse( diff --git a/src/main/java/me/kavin/piped/utils/ResponseHelper.java b/src/main/java/me/kavin/piped/utils/ResponseHelper.java index 296ca1f..f00928e 100644 --- a/src/main/java/me/kavin/piped/utils/ResponseHelper.java +++ b/src/main/java/me/kavin/piped/utils/ResponseHelper.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.Scheduler; +import com.grack.nanojson.JsonObject; +import com.grack.nanojson.JsonWriter; import com.rometools.rome.feed.synd.*; import com.rometools.rome.io.FeedException; import com.rometools.rome.io.SyndFeedOutput; @@ -41,6 +43,7 @@ import org.schabi.newpipe.extractor.search.SearchInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.extractor.utils.JsonUtils; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -48,14 +51,18 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import static java.nio.charset.StandardCharsets.UTF_8; import static me.kavin.piped.consts.Constants.YOUTUBE_SERVICE; import static me.kavin.piped.utils.URLUtils.rewriteURL; import static me.kavin.piped.utils.URLUtils.substringYouTube; +import static org.schabi.newpipe.extractor.NewPipe.getPreferredContentCountry; +import static org.schabi.newpipe.extractor.NewPipe.getPreferredLocalization; +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse; +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; public class ResponseHelper { @@ -162,6 +169,22 @@ public class ResponseHelper { } + public static byte[] resolveClipId(String clipId) throws Exception { + + final byte[] body = JsonWriter.string(prepareDesktopJsonBuilder( + getPreferredLocalization(), getPreferredContentCountry()) + .value("url", "https://www.youtube.com/clip/" + clipId) + .done()) + .getBytes(UTF_8); + + final JsonObject jsonResponse = getJsonPostResponse("navigation/resolve_url", + body, getPreferredLocalization()); + + final String videoId = JsonUtils.getString(jsonResponse, "endpoint.watchEndpoint.videoId"); + + return Constants.mapper.writeValueAsBytes(new VideoResolvedResponse(videoId)); + } + public static byte[] channelResponse(String channelPath) throws Exception { final ChannelInfo info = ChannelInfo.getInfo("https://youtube.com/" + channelPath); @@ -336,7 +359,7 @@ public class ResponseHelper { feed.setEntries(entries); - return new SyndFeedOutput().outputString(feed).getBytes(StandardCharsets.UTF_8); + return new SyndFeedOutput().outputString(feed).getBytes(UTF_8); } @@ -789,7 +812,7 @@ public class ResponseHelper { s.close(); - return new SyndFeedOutput().outputString(feed).getBytes(StandardCharsets.UTF_8); + return new SyndFeedOutput().outputString(feed).getBytes(UTF_8); } s.close(); diff --git a/src/main/java/me/kavin/piped/utils/resp/VideoResolvedResponse.java b/src/main/java/me/kavin/piped/utils/resp/VideoResolvedResponse.java new file mode 100644 index 0000000..3c89d0b --- /dev/null +++ b/src/main/java/me/kavin/piped/utils/resp/VideoResolvedResponse.java @@ -0,0 +1,10 @@ +package me.kavin.piped.utils.resp; + +public class VideoResolvedResponse { + + public String videoId; + + public VideoResolvedResponse(String videoId) { + this.videoId = videoId; + } +}