Commit changes.

This commit is contained in:
FireMasterK 2021-02-24 15:22:29 +05:30
parent 947d6d5787
commit dd2ee203bb
No known key found for this signature in database
GPG Key ID: 8DFF5DD33E93DB58
22 changed files with 650 additions and 531 deletions

View File

@ -19,7 +19,7 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:1.67'
implementation 'org.mongodb:mongodb-driver-sync:4.2.1'
implementation 'io.projectreactor.netty:reactor-netty:1.0.4'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.20.8'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:SNAPSHOT'
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.fasterxml.jackson.core:jackson-core:2.12.1'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.12.1'
@ -27,6 +27,8 @@ dependencies {
implementation 'org.json:json:20201115'
implementation 'com.github.ben-manes.caffeine:caffeine:2.8.6'
implementation 'com.rometools:rome:1.15.0'
implementation 'com.github.ipfs:java-ipfs-http-client:v1.3.3'
implementation 'net.java.dev.jna:jna-platform:5.7.0'
}
shadowJar {

View File

@ -1,6 +1,13 @@
package me.kavin.piped;
import io.netty.util.AsciiString;
import static io.netty.handler.codec.http.HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN;
import static io.netty.handler.codec.http.HttpHeaderNames.CACHE_CONTROL;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN;
import java.nio.charset.StandardCharsets;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.Localization;
@ -11,6 +18,7 @@ import com.rometools.rome.io.XmlReader;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.AsciiString;
import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.DownloaderImpl;
import me.kavin.piped.utils.ResponseHelper;
@ -22,231 +30,233 @@ import reactor.netty.NettyOutbound;
import reactor.netty.http.server.HttpServer;
import reactor.netty.http.server.HttpServerResponse;
import java.nio.charset.StandardCharsets;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpHeaderValues.*;
public class Main {
public static void main(String[] args) throws Exception {
NewPipe.init(new DownloaderImpl(), new Localization("en", "US"));
NewPipe.init(new DownloaderImpl(), new Localization("en", "US"));
DisposableServer server = HttpServer.create().port(Constants.PORT).route(routes -> {
routes.get("/webhooks/pubsub", (req, res) -> {
routes.get("/webhooks/pubsub", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res, query.parameters().get("hub.challenge").get(0), TEXT_PLAIN, 200, "private", start);
try {
return writeResponse(res, query.parameters().get("hub.challenge").get(0), TEXT_PLAIN, 200,
"private", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.post("/webhooks/pubsub", (req, res) -> {
routes.post("/webhooks/pubsub", (req, res) -> {
long start = System.nanoTime();
long start = System.nanoTime();
try {
req.receive().asInputStream().subscribe(in -> {
try {
SyndFeed feed = new SyndFeedInput().build(new XmlReader(in));
try {
req.receive().asInputStream().subscribe(in -> {
try {
SyndFeed feed = new SyndFeedInput().build(new XmlReader(in));
feed.getEntries().forEach(entry -> {
System.out.println(entry.getLinks().get(0).getHref());
System.out.println(entry.getAuthors().get(0).getUri());
});
feed.getEntries().forEach(entry -> {
System.out.println(entry.getLinks().get(0).getHref());
System.out.println(entry.getAuthors().get(0).getUri());
});
} catch (Exception e) {
e.printStackTrace();
}
});
return writeResponse(res, "ok", TEXT_PLAIN, 200, "private", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
} catch (Exception e) {
e.printStackTrace();
}
});
return writeResponse(res, "ok", TEXT_PLAIN, 200, "private", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/sponsors/{videoId}", (req, res) -> {
routes.get("/sponsors/{videoId}", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res, SponsorBlockUtils.getSponsors(req.param("videoId"),
query.parameters().get("category").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res, SponsorBlockUtils.getSponsors(req.param("videoId"),
query.parameters().get("category").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/streams/{videoId}", (req, res) -> {
routes.get("/streams/{videoId}", (req, res) -> {
long start = System.nanoTime();
try {
// The stream links are valid for 6 hours.
return writeResponse(res, ResponseHelper.streamsResponse(req.param("videoId")), 200,
"public, s-maxage=21540", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
long start = System.nanoTime();
try {
// The stream links are valid for 6 hours.
return writeResponse(res, ResponseHelper.streamsResponse(req.param("videoId")), 200,
"public, s-maxage=21540", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/channels/{channelId}", (req, res) -> {
routes.get("/channels/{channelId}", (req, res) -> {
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.channelResponse(req.param("channelId")), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.channelResponse(req.param("channelId")), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/nextpage/channels/{channelId}", (req, res) -> {
routes.get("/nextpage/channels/{channelId}", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res, ResponseHelper.channelPageResponse(req.param("channelId"),
query.parameters().get("url").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res, ResponseHelper.channelPageResponse(req.param("channelId"),
query.parameters().get("url").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/playlists/{playlistId}", (req, res) -> {
routes.get("/playlists/{playlistId}", (req, res) -> {
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.playlistResponse(req.param("playlistId")), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.playlistResponse(req.param("playlistId")), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/nextpage/playlists/{playlistId}", (req, res) -> {
routes.get("/nextpage/playlists/{playlistId}", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res, ResponseHelper.playlistPageResponse(req.param("playlistId"),
query.parameters().get("url").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res, ResponseHelper.playlistPageResponse(req.param("playlistId"),
query.parameters().get("url").get(0)), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/suggestions", (req, res) -> {
routes.get("/suggestions", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res,
ResponseHelper.suggestionsResponse(query.parameters().get("query").get(0)), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res,
ResponseHelper.suggestionsResponse(query.parameters().get("query").get(0)), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/search", (req, res) -> {
routes.get("/search", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res, ResponseHelper.searchResponse(query.parameters().get("q").get(0)), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res, ResponseHelper.searchResponse(query.parameters().get("q").get(0)), 200,
"public, max-age=600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/nextpage/search", (req, res) -> {
routes.get("/nextpage/search", (req, res) -> {
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
long start = System.nanoTime();
QueryStringDecoder query = new QueryStringDecoder(req.uri());
try {
return writeResponse(res,
ResponseHelper.searchPageResponse(query.parameters().get("q").get(0),
query.parameters().get("url").get(0), query.parameters().get("id").get(0)),
200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
try {
return writeResponse(res,
ResponseHelper.searchPageResponse(query.parameters().get("q").get(0),
query.parameters().get("url").get(0), query.parameters().get("id").get(0)),
200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
routes.get("/trending", (req, res) -> {
routes.get("/trending", (req, res) -> {
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.trendingResponse(), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
long start = System.nanoTime();
try {
return writeResponse(res, ResponseHelper.trendingResponse(), 200, "public, max-age=3600", start);
} catch (Exception e) {
e.printStackTrace();
return writeResponse(res, ExceptionUtils.getStackTrace(e), 500, "private", start);
}
});
});
}).compress(true).bindNow();
server.onDispose().block();
}
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, int code, String cache, long time) {
return writeResponse(res, resp, APPLICATION_JSON, code, cache, time);
}
}).bindNow();
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, AsciiString mimeType, int code, String cache, long time) {
return writeResponse(res, resp, mimeType.toString(), code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, String mimeType, int code, String cache, long time) {
return writeResponse(res, resp.getBytes(StandardCharsets.UTF_8), mimeType, code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, byte[] resp, int code, String cache, long time) {
return writeResponse(res, resp, APPLICATION_JSON, code, cache, time);
Thread.sleep(Long.MAX_VALUE);
}
public static NettyOutbound writeResponse(HttpServerResponse res, byte[] resp, AsciiString mimeType, int code, String cache, long time) {
return writeResponse(res, resp, mimeType.toString(), code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, int code, String cache, long time) {
return writeResponse(res, resp, APPLICATION_JSON, code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, AsciiString mimeType, int code,
String cache, long time) {
return writeResponse(res, resp, mimeType.toString(), code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, String resp, String mimeType, int code,
String cache, long time) {
return writeResponse(res, resp.getBytes(StandardCharsets.UTF_8), mimeType, code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, byte[] resp, int code, String cache, long time) {
return writeResponse(res, resp, APPLICATION_JSON, code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, byte[] resp, AsciiString mimeType, int code,
String cache, long time) {
return writeResponse(res, resp, mimeType.toString(), code, cache, time);
}
public static NettyOutbound writeResponse(HttpServerResponse res, byte[] resp, String mimeType, int code, String cache, long time) {
return res
@ -258,15 +268,14 @@ public class Main {
.sendByteArray(Flux.just(resp));
}
public static NettyOutbound writeResponse(HttpServerResponse res, Flux<String> resp, int code, String cache) {
return writeResponse(res, resp, APPLICATION_JSON, code, cache);
return writeResponse(res, resp, APPLICATION_JSON, code, cache);
}
public static NettyOutbound writeResponse(HttpServerResponse res, Flux<String> resp, AsciiString mimeType, int code, String cache) {
return writeResponse(res, resp, mimeType.toString(), code, cache);
}
public static NettyOutbound writeResponse(HttpServerResponse res, Flux<String> resp, AsciiString mimeType, int code,
String cache) {
return writeResponse(res, resp, mimeType.toString(), code, cache);
}
public static NettyOutbound writeResponse(HttpServerResponse res, Flux<String> resp, String mimeType, int code, String cache) {
return res

View File

@ -10,6 +10,8 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
public class Constants {
@ -24,21 +26,24 @@ public class Constants {
public static final StreamingService YOUTUBE_SERVICE;
public static final HttpClient h2client = HttpClient.newBuilder().followRedirects(Redirect.NORMAL)
.version(Version.HTTP_2).build();
.version(Version.HTTP_2).build();
// public static final HttpClient h3client = Http3ClientBuilder.newBuilder().followRedirects(Redirect.NORMAL).build();
public static final MongoClient mongoClient;
public static final ObjectMapper mapper = new ObjectMapper();
static {
Properties prop = new Properties();
try {
YOUTUBE_SERVICE = NewPipe.getService(0);
prop.load(new FileReader("config.properties"));
Properties prop = new Properties();
try {
YOUTUBE_SERVICE = NewPipe.getService(0);
prop.load(new FileReader("config.properties"));
PORT = Integer.parseInt(prop.getProperty("PORT"));
PROXY_PART = prop.getProperty("PROXY_PART");
} catch (Exception e) {
throw new RuntimeException(e);
}
PORT = Integer.parseInt(prop.getProperty("PORT"));
PROXY_PART = prop.getProperty("PROXY_PART");
mongoClient = MongoClients.create(prop.getProperty("MONGO_URI"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,20 @@
package me.kavin.piped.ipfs;
import me.kavin.piped.utils.Multithreading;
import me.kavin.piped.utils.obj.Channel;
public class IPFS {
// private static final io.ipfs.api.IPFS ipfs = new io.ipfs.api.IPFS(new MultiAddress("/ip4/127.0.0.1/tcp/5001"));
public static void publishData(final Channel channel) {
Multithreading.runAsync(() -> {
try {
// ipfs.pubsub.pub(URLUtils.silentEncode(channel.id),
// URLUtils.silentEncode(Constants.mapper.writeValueAsString(channel)));
} catch (Exception e) {
e.printStackTrace();
}
});
}
}

View File

@ -9,13 +9,13 @@ public class CookieHandler extends java.net.CookieHandler {
@Override
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException {
// TODO Auto-generated method stub
return null;
// TODO Auto-generated method stub
return null;
}
@Override
public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
}
}

View File

@ -23,32 +23,32 @@ public class DownloaderImpl extends Downloader {
@Override
public Response execute(Request request) throws IOException, ReCaptchaException {
// TODO: HTTP/3 aka QUIC
Builder builder = HttpRequest.newBuilder(URI.create(request.url()));
// TODO: HTTP/3 aka QUIC
Builder builder = HttpRequest.newBuilder(URI.create(request.url()));
byte[] data = request.dataToSend();
BodyPublisher publisher = data == null ? HttpRequest.BodyPublishers.noBody()
: HttpRequest.BodyPublishers.ofByteArray(data);
byte[] data = request.dataToSend();
BodyPublisher publisher = data == null ? HttpRequest.BodyPublishers.noBody()
: HttpRequest.BodyPublishers.ofByteArray(data);
builder.method(request.httpMethod(), publisher);
request.headers().forEach((name, values) -> values.forEach(value -> builder.header(name, value)));
builder.method(request.httpMethod(), publisher);
request.headers().forEach((name, values) -> values.forEach(value -> builder.header(name, value)));
builder.setHeader("User-Agent", Constants.USER_AGENT);
builder.setHeader("User-Agent", Constants.USER_AGENT);
HttpResponse<String> response = null;
HttpResponse<String> response = null;
try {
response = Constants.h2client.send(builder.build(), BodyHandlers.ofString());
} catch (InterruptedException e) {
// ignored
}
try {
response = Constants.h2client.send(builder.build(), BodyHandlers.ofString());
} catch (InterruptedException e) {
// ignored
}
// TODO: Implement solver
if (response.statusCode() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested", String.valueOf(response.uri()));
}
// TODO: Implement solver
if (response.statusCode() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested", String.valueOf(response.uri()));
}
return new Response(response.statusCode(), "UNDEFINED", response.headers().map(), response.body(),
String.valueOf(response.uri()));
return new Response(response.statusCode(), "UNDEFINED", response.headers().map(), response.body(),
String.valueOf(response.uri()));
}
}

View File

@ -0,0 +1,19 @@
package me.kavin.piped.utils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Multithreading {
private static final ExecutorService es = Executors.newFixedThreadPool(16);
public static void runAsync(final Runnable runnable) {
es.submit(runnable);
}
public static void runAsyncTimeout(final Runnable runnable) {
es.submit(runnable);
}
}

View File

@ -0,0 +1,7 @@
package me.kavin.piped.utils;
public class RegisterRequest {
public String username, password;
}

View File

@ -10,7 +10,6 @@ import java.net.http.HttpResponse.BodyHandlers;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
@ -34,7 +33,9 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.kavin.piped.consts.Constants;
import me.kavin.piped.ipfs.IPFS;
import me.kavin.piped.utils.obj.Channel;
import me.kavin.piped.utils.obj.ChapterSegment;
import me.kavin.piped.utils.obj.PipedStream;
import me.kavin.piped.utils.obj.Playlist;
import me.kavin.piped.utils.obj.SearchResults;
@ -48,353 +49,361 @@ import me.kavin.piped.utils.obj.search.SearchStream;
public class ResponseHelper {
public static final LoadingCache<String, CommentsInfo> commentsCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS).maximumSize(1000)
.build(key -> CommentsInfo.getInfo("https://www.youtube.com/watch?v=" + key));
.expireAfterWrite(1, TimeUnit.HOURS).maximumSize(1000)
.build(key -> CommentsInfo.getInfo("https://www.youtube.com/watch?v=" + key));
public static final byte[] streamsResponse(String videoId) throws Exception {
CompletableFuture<StreamInfo> futureStream = CompletableFuture.supplyAsync(() -> {
try {
return StreamInfo.getInfo("https://www.youtube.com/watch?v=" + videoId);
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
return null;
});
CompletableFuture<StreamInfo> futureStream = CompletableFuture.supplyAsync(() -> {
try {
return StreamInfo.getInfo("https://www.youtube.com/watch?v=" + videoId);
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
return null;
});
CompletableFuture<String> futureLBRY = CompletableFuture.supplyAsync(() -> {
try {
return getLBRYStreamURL(videoId);
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
return null;
});
CompletableFuture<String> futureLBRY = CompletableFuture.supplyAsync(() -> {
try {
return getLBRYStreamURL(videoId);
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
return null;
});
final List<Subtitle> subtitles = new ObjectArrayList<>();
final List<Subtitle> subtitles = new ObjectArrayList<>();
final StreamInfo info = futureStream.get();
final StreamInfo info = futureStream.get();
info.getSubtitles().forEach(subtitle -> subtitles
.add(new Subtitle(rewriteURL(subtitle.getUrl()), subtitle.getFormat().getMimeType())));
// System.out.println(Constants.mapper.writeValueAsString(info.getStreamSegments()));
info.getSubtitles().forEach(subtitle -> subtitles
.add(new Subtitle(rewriteURL(subtitle.getUrl()), subtitle.getFormat().getMimeType())));
final List<PipedStream> videoStreams = new ObjectArrayList<>();
final List<PipedStream> audioStreams = new ObjectArrayList<>();
final List<PipedStream> videoStreams = new ObjectArrayList<>();
final List<PipedStream> audioStreams = new ObjectArrayList<>();
final String lbryURL = futureLBRY.get();
final String lbryURL = futureLBRY.get();
if (lbryURL != null)
videoStreams.add(new PipedStream(lbryURL, "MP4", "LBRY", "video/mp4", false));
if (lbryURL != null)
videoStreams.add(new PipedStream(lbryURL, "MP4", "LBRY", "video/mp4", false));
final String hls;
boolean livestream = false;
final String hls;
boolean livestream = false;
if ((hls = info.getHlsUrl()) != null && !hls.isEmpty())
livestream = true;
if ((hls = info.getHlsUrl()) != null && !hls.isEmpty())
livestream = true;
if (hls != null) {
if (hls != null) {
java.util.stream.Stream<String> resp = Constants.h2client
.send(HttpRequest.newBuilder(URI.create(hls)).GET().build(), BodyHandlers.ofLines()).body();
ObjectArrayList<String> lines = new ObjectArrayList<>();
resp.forEach(line -> lines.add(line));
java.util.stream.Stream<String> resp = Constants.h2client
.send(HttpRequest.newBuilder(URI.create(hls)).GET().build(), BodyHandlers.ofLines()).body();
ObjectArrayList<String> lines = new ObjectArrayList<>();
resp.forEach(line -> lines.add(line));
for (int i = lines.size() - 1; i > 2; i--) {
String line = lines.get(i);
if (line.startsWith("https://manifest.googlevideo.com")) {
String prevLine = lines.get(i - 1);
String height = StringUtils.substringBetween(prevLine, "RESOLUTION=", ",").split("x")[1];
int fps = Integer.parseInt(StringUtils.substringBetween(prevLine, "FRAME-RATE=", ","));
String quality = height + "p";
if (fps > 30)
quality += fps;
videoStreams.add(new PipedStream(line, "HLS", quality, "application/x-mpegURL", false));
}
}
}
for (int i = lines.size() - 1; i > 2; i--) {
String line = lines.get(i);
if (line.startsWith("https://manifest.googlevideo.com")) {
String prevLine = lines.get(i - 1);
String height = StringUtils.substringBetween(prevLine, "RESOLUTION=", ",").split("x")[1];
int fps = Integer.parseInt(StringUtils.substringBetween(prevLine, "FRAME-RATE=", ","));
String quality = height + "p";
if (fps > 30)
quality += fps;
videoStreams.add(new PipedStream(line, "HLS", quality, "application/x-mpegURL", false));
}
}
}
if (!livestream) {
info.getVideoOnlyStreams()
.forEach(stream -> videoStreams
.add(new PipedStream(rewriteURL(stream.getUrl()), String.valueOf(stream.getFormat()),
stream.getResolution(), stream.getFormat().getMimeType(), true)));
info.getVideoStreams()
.forEach(stream -> videoStreams
.add(new PipedStream(rewriteURL(stream.getUrl()), String.valueOf(stream.getFormat()),
stream.getResolution(), stream.getFormat().getMimeType(), false)));
if (!livestream) {
info.getVideoOnlyStreams().forEach(stream -> videoStreams.add(new PipedStream(rewriteURL(stream.getUrl()),
String.valueOf(stream.getFormat()), stream.getResolution(), stream.getFormat().getMimeType(), true,
stream.getBitrate(), stream.getInitStart(), stream.getInitEnd(), stream.getIndexStart(),
stream.getIndexEnd(), stream.getCodec(), stream.getWidth(), stream.getHeight(), 30)));
info.getVideoStreams()
.forEach(stream -> videoStreams
.add(new PipedStream(rewriteURL(stream.getUrl()), String.valueOf(stream.getFormat()),
stream.getResolution(), stream.getFormat().getMimeType(), false)));
info.getAudioStreams()
.forEach(stream -> audioStreams
.add(new PipedStream(rewriteURL(stream.getUrl()), String.valueOf(stream.getFormat()),
stream.getAverageBitrate() + " kbps", stream.getFormat().getMimeType(), false)));
}
info.getAudioStreams()
.forEach(stream -> audioStreams.add(new PipedStream(rewriteURL(stream.getUrl()),
String.valueOf(stream.getFormat()), stream.getAverageBitrate() + " kbps",
stream.getFormat().getMimeType(), false, stream.getBitrate(), stream.getInitStart(),
stream.getInitEnd(), stream.getIndexStart(), stream.getIndexEnd(), stream.getCodec())));
}
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
info.getRelatedStreams().forEach(o -> {
StreamInfoItem item = (StreamInfoItem) o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
info.getRelatedStreams().forEach(o -> {
StreamInfoItem item = (StreamInfoItem) o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
final Streams streams = new Streams(info.getName(), info.getDescription().getContent(),
info.getTextualUploadDate(), info.getUploaderName(), info.getUploaderUrl().substring(23),
rewriteURL(info.getUploaderAvatarUrl()), rewriteURL(info.getThumbnailUrl()), info.getDuration(),
info.getViewCount(), info.getLikeCount(), info.getDislikeCount(), audioStreams, videoStreams,
relatedStreams, subtitles, livestream, hls);
List<ChapterSegment> segments = new ObjectArrayList<>();
return Constants.mapper.writeValueAsBytes(streams);
info.getStreamSegments().forEach(
segment -> segments.add(new ChapterSegment(segment.getTitle(), segment.getStartTimeSeconds())));
final Streams streams = new Streams(info.getName(), info.getDescription().getContent(),
info.getTextualUploadDate(), info.getUploaderName(), info.getUploaderUrl().substring(23),
rewriteURL(info.getUploaderAvatarUrl()), rewriteURL(info.getThumbnailUrl()), info.getDuration(),
info.getViewCount(), info.getLikeCount(), info.getDislikeCount(), audioStreams, videoStreams,
relatedStreams, subtitles, livestream, hls);
return Constants.mapper.writeValueAsBytes(streams);
}
public static final byte[] channelResponse(String channelId)
throws IOException, ExtractionException, InterruptedException {
public static final byte[] channelResponse(String channelId) throws Exception {
final ChannelInfo info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId);
final ChannelInfo info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId);
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
String nextpage = info.hasNextPage() ? info.getNextPage().getUrl() : null;
String nextpage = info.hasNextPage() ? info.getNextPage().getUrl() : null;
final Channel channel = new Channel(info.getName(), rewriteURL(info.getAvatarUrl()),
rewriteURL(info.getBannerUrl()), info.getDescription(), nextpage, relatedStreams);
final Channel channel = new Channel(info.getId(), info.getName(), rewriteURL(info.getAvatarUrl()),
rewriteURL(info.getBannerUrl()), info.getDescription(), nextpage, relatedStreams);
return Constants.mapper.writeValueAsBytes(channel);
IPFS.publishData(channel);
return Constants.mapper.writeValueAsBytes(channel);
}
public static final byte[] channelPageResponse(String channelId, String url)
throws IOException, ExtractionException, InterruptedException {
throws IOException, ExtractionException, InterruptedException {
InfoItemsPage<StreamInfoItem> page = ChannelInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
"https://youtube.com/channel/" + channelId, new Page(url));
InfoItemsPage<StreamInfoItem> page = ChannelInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
"https://youtube.com/channel/" + channelId, new Page(url));
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
page.getItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
page.getItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
String nextpage = page.hasNextPage() ? page.getNextPage().getUrl() : null;
String nextpage = page.hasNextPage() ? page.getNextPage().getUrl() : null;
final StreamsPage streamspage = new StreamsPage(nextpage, relatedStreams);
final StreamsPage streamspage = new StreamsPage(nextpage, relatedStreams);
return Constants.mapper.writeValueAsBytes(streamspage);
return Constants.mapper.writeValueAsBytes(streamspage);
}
public static final byte[] trendingResponse() throws ParsingException, ExtractionException, IOException {
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
String url = Constants.YOUTUBE_SERVICE.getKioskList().getListLinkHandlerFactoryByType("Trending")
.getUrl("Trending");
KioskInfo info = KioskInfo.getInfo(Constants.YOUTUBE_SERVICE, url);
String url = Constants.YOUTUBE_SERVICE.getKioskList().getListLinkHandlerFactoryByType("Trending")
.getUrl("Trending");
KioskInfo info = KioskInfo.getInfo(Constants.YOUTUBE_SERVICE, url);
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
return Constants.mapper.writeValueAsBytes(relatedStreams);
return Constants.mapper.writeValueAsBytes(relatedStreams);
}
public static final byte[] playlistResponse(String playlistId)
throws IOException, ExtractionException, InterruptedException {
throws IOException, ExtractionException, InterruptedException {
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<StreamItem> relatedStreams = new ObjectArrayList<>();
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
info.getRelatedItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
String nextpage = info.hasNextPage() ? info.getNextPage().getUrl() : null;
String nextpage = info.hasNextPage() ? info.getNextPage().getUrl() : null;
final Playlist playlist = new Playlist(info.getName(), rewriteURL(info.getThumbnailUrl()),
rewriteURL(info.getBannerUrl()), nextpage, info.getUploaderName(), info.getUploaderUrl().substring(23),
rewriteURL(info.getUploaderAvatarUrl()), (int) info.getStreamCount(), relatedStreams);
final Playlist playlist = new Playlist(info.getName(), rewriteURL(info.getThumbnailUrl()),
rewriteURL(info.getBannerUrl()), nextpage, info.getUploaderName(), info.getUploaderUrl().substring(23),
rewriteURL(info.getUploaderAvatarUrl()), (int) info.getStreamCount(), relatedStreams);
return Constants.mapper.writeValueAsBytes(playlist);
return Constants.mapper.writeValueAsBytes(playlist);
}
public static final byte[] playlistPageResponse(String playlistId, String url)
throws IOException, ExtractionException, InterruptedException {
throws IOException, ExtractionException, InterruptedException {
InfoItemsPage<StreamInfoItem> page = PlaylistInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
"https://www.youtube.com/playlist?list=" + playlistId, new Page(url));
InfoItemsPage<StreamInfoItem> page = PlaylistInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
"https://www.youtube.com/playlist?list=" + playlistId, new Page(url));
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
page.getItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
page.getItems().forEach(o -> {
StreamInfoItem item = o;
relatedStreams.add(new StreamItem(item.getUrl().substring(23), item.getName(),
rewriteURL(item.getThumbnailUrl()), item.getUploaderName(), item.getUploaderUrl().substring(23),
item.getTextualUploadDate(), item.getDuration(), item.getViewCount()));
});
String nextpage = page.hasNextPage() ? page.getNextPage().getUrl() : null;
String nextpage = page.hasNextPage() ? page.getNextPage().getUrl() : null;
final StreamsPage streamspage = new StreamsPage(nextpage, relatedStreams);
final StreamsPage streamspage = new StreamsPage(nextpage, relatedStreams);
return Constants.mapper.writeValueAsBytes(streamspage);
return Constants.mapper.writeValueAsBytes(streamspage);
}
public static final byte[] suggestionsResponse(String query)
throws JsonProcessingException, IOException, ExtractionException {
throws JsonProcessingException, IOException, ExtractionException {
return Constants.mapper
.writeValueAsBytes(Constants.YOUTUBE_SERVICE.getSuggestionExtractor().suggestionList(query));
return Constants.mapper
.writeValueAsBytes(Constants.YOUTUBE_SERVICE.getSuggestionExtractor().suggestionList(query));
}
public static final byte[] searchResponse(String q) throws IOException, ExtractionException, InterruptedException {
final SearchInfo info = SearchInfo.getInfo(Constants.YOUTUBE_SERVICE,
Constants.YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q));
final SearchInfo info = SearchInfo.getInfo(Constants.YOUTUBE_SERVICE,
Constants.YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q));
ObjectArrayList<SearchItem> items = new ObjectArrayList<>();
ObjectArrayList<SearchItem> items = new ObjectArrayList<>();
info.getRelatedItems().forEach(item -> {
switch (item.getInfoType()) {
case STREAM:
StreamInfoItem stream = (StreamInfoItem) item;
items.add(new SearchStream(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23), stream.getViewCount(), stream.getDuration()));
break;
case CHANNEL:
items.add(new SearchItem(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23)));
break;
default:
break;
}
});
info.getRelatedItems().forEach(item -> {
switch (item.getInfoType()) {
case STREAM:
StreamInfoItem stream = (StreamInfoItem) item;
items.add(new SearchStream(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23), stream.getViewCount(), stream.getDuration()));
break;
case CHANNEL:
items.add(new SearchItem(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23)));
break;
default:
break;
}
});
Page nextpage = info.getNextPage();
Page nextpage = info.getNextPage();
return nextpage != null
? Constants.mapper.writeValueAsBytes(new SearchResults(nextpage.getUrl(), nextpage.getId(), items))
: Constants.mapper.writeValueAsBytes(new SearchResults(null, null, items));
return nextpage != null
? Constants.mapper.writeValueAsBytes(new SearchResults(nextpage.getUrl(), nextpage.getId(), items))
: Constants.mapper.writeValueAsBytes(new SearchResults(null, null, items));
}
public static final byte[] searchPageResponse(String q, String url, String id)
throws IOException, ExtractionException, InterruptedException {
throws IOException, ExtractionException, InterruptedException {
InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
Constants.YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q), new Page(url, id));
InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(Constants.YOUTUBE_SERVICE,
Constants.YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q), new Page(url, id));
ObjectArrayList<SearchItem> items = new ObjectArrayList<>();
ObjectArrayList<SearchItem> items = new ObjectArrayList<>();
pages.getItems().forEach(item -> {
switch (item.getInfoType()) {
case STREAM:
StreamInfoItem stream = (StreamInfoItem) item;
items.add(new SearchStream(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23), stream.getViewCount(), stream.getDuration()));
break;
case CHANNEL:
items.add(new SearchItem(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23)));
break;
default:
break;
}
});
pages.getItems().forEach(item -> {
switch (item.getInfoType()) {
case STREAM:
StreamInfoItem stream = (StreamInfoItem) item;
items.add(new SearchStream(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23), stream.getViewCount(), stream.getDuration()));
break;
case CHANNEL:
items.add(new SearchItem(item.getName(), rewriteURL(item.getThumbnailUrl()),
item.getUrl().substring(23)));
break;
default:
break;
}
});
Page nextpage = pages.getNextPage();
Page nextpage = pages.getNextPage();
return nextpage != null
? Constants.mapper.writeValueAsBytes(new SearchResults(nextpage.getUrl(), nextpage.getId(), items))
: Constants.mapper.writeValueAsBytes(new SearchResults(null, null, items));
return nextpage != null
? Constants.mapper.writeValueAsBytes(new SearchResults(nextpage.getUrl(), nextpage.getId(), items))
: Constants.mapper.writeValueAsBytes(new SearchResults(null, null, items));
}
public static final byte[] registerResponse(String user, String pass) throws IOException {
return Constants.mapper.writeValueAsBytes(null);
return Constants.mapper.writeValueAsBytes(null);
}
private static final String getLBRYStreamURL(String videoId) throws IOException, InterruptedException {
String lbryId = new JSONObject(Constants.h2client.send(HttpRequest
.newBuilder(URI.create("https://api.lbry.com/yt/resolve?video_ids=" + URLUtils.silentEncode(videoId)))
.setHeader("User-Agent", Constants.USER_AGENT).build(), BodyHandlers.ofString()).body())
.getJSONObject("data").getJSONObject("videos").optString(videoId);
String lbryId = new JSONObject(Constants.h2client.send(HttpRequest
.newBuilder(URI.create("https://api.lbry.com/yt/resolve?video_ids=" + URLUtils.silentEncode(videoId)))
.setHeader("User-Agent", Constants.USER_AGENT).build(), BodyHandlers.ofString()).body())
.getJSONObject("data").getJSONObject("videos").optString(videoId);
if (!lbryId.isEmpty())
return rewriteURL(
new JSONObject(
Constants.h2client.send(
HttpRequest.newBuilder(URI.create("https://api.lbry.tv/api/v1/proxy?m=get"))
.POST(BodyPublishers.ofString(String.valueOf(new JSONObject()
.put("jsonrpc", "2.0").put("method", "get").put("params",
new JSONObject().put("uri", "lbry://" + lbryId)
.put("save_file", true)))))
.build(),
BodyHandlers.ofString()).body()).getJSONObject("result")
.getString("streaming_url"));
if (!lbryId.isEmpty())
return rewriteURL(
new JSONObject(
Constants.h2client.send(
HttpRequest.newBuilder(URI.create("https://api.lbry.tv/api/v1/proxy?m=get"))
.POST(BodyPublishers.ofString(String.valueOf(new JSONObject()
.put("jsonrpc", "2.0").put("method", "get").put("params",
new JSONObject().put("uri", "lbry://" + lbryId)
.put("save_file", true)))))
.build(),
BodyHandlers.ofString()).body()).getJSONObject("result")
.getString("streaming_url"));
return null;
return null;
}
private static String rewriteURL(final String old) {
if (Constants.debug)
return old;
if (Constants.debug)
return old;
if (old == null || old.isEmpty())
return null;
if (old == null || old.isEmpty())
return null;
URL url = null;
URL url = null;
try {
url = new URL(old);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
url = new URL(old);
} catch (MalformedURLException e) {
e.printStackTrace();
}
final String host = url.getHost();
final String host = url.getHost();
String query = url.getQuery();
String query = url.getQuery();
boolean hasQuery = query != null;
boolean hasQuery = query != null;
String path = url.getPath();
String path = url.getPath();
path = path.replace("-rj", "-rw");
path = path.replace("-rj", "-rw");
if (path.startsWith("/vi/") && !path.contains("_live")) {
path = path.replace("/vi/", "/vi_webp/").replace(".jpg", ".webp").replace("hq720", "mqdefault")
.replace("hqdefault", "mqdefault");
if (path.startsWith("/vi/") && !path.contains("_live")) {
path = path.replace("/vi/", "/vi_webp/").replace(".jpg", ".webp").replace("hq720", "mqdefault")
.replace("hqdefault", "mqdefault");
hasQuery = false;
}
hasQuery = false;
}
return Constants.PROXY_PART + path + (hasQuery ? "?" + query + "&host=" : "?host=")
+ URLUtils.silentEncode(host);
return Constants.PROXY_PART + path + (hasQuery ? "?" + query + "&host=" : "?host=")
+ URLUtils.silentEncode(host);
}
}

View File

@ -19,36 +19,36 @@ import me.kavin.piped.consts.Constants;
public class SponsorBlockUtils {
public static final String getSponsors(String id, String categories)
throws IOException, InterruptedException, NoSuchAlgorithmException, JsonParserException {
throws IOException, InterruptedException, NoSuchAlgorithmException, JsonParserException {
String hash = toSha256(id);
String hash = toSha256(id);
URI uri = URI.create("https://sponsor.ajay.app/api/skipSegments/" + URLUtils.silentEncode(hash.substring(0, 4))
+ "?categories=" + URLUtils.silentEncode(categories));
URI uri = URI.create("https://sponsor.ajay.app/api/skipSegments/" + URLUtils.silentEncode(hash.substring(0, 4))
+ "?categories=" + URLUtils.silentEncode(categories));
JsonArray jArray = JsonParser.array()
.from(Constants.h2client.send(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()).body());
JsonArray jArray = JsonParser.array()
.from(Constants.h2client.send(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()).body());
jArray.removeIf(jObject -> !((JsonObject) jObject).getString("videoID").equalsIgnoreCase(id));
jArray.removeIf(jObject -> !((JsonObject) jObject).getString("videoID").equalsIgnoreCase(id));
return JsonWriter.string(jArray.getObject(0));
return JsonWriter.string(jArray.getObject(0));
}
private static final String toSha256(final String videoId) throws NoSuchAlgorithmException {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] bytes = digest.digest(videoId.getBytes(StandardCharsets.UTF_8));
final StringBuilder sb = new StringBuilder();
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] bytes = digest.digest(videoId.getBytes(StandardCharsets.UTF_8));
final StringBuilder sb = new StringBuilder();
for (final byte b : bytes) {
final String hex = Integer.toHexString(0xff & b);
for (final byte b : bytes) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
sb.append('0');
}
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
sb.append(hex);
}
return sb.toString();
return sb.toString();
}
}

View File

@ -5,11 +5,11 @@ import java.net.URLEncoder;
public class URLUtils {
public static String silentEncode(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (Exception e) {
// ignored
}
return s;
try {
return URLEncoder.encode(s, "UTF-8");
} catch (Exception e) {
// ignored
}
return s;
}
}

View File

@ -4,16 +4,17 @@ import java.util.List;
public class Channel {
public String name, avatarUrl, bannerUrl, description, nextpage;
public String id, name, avatarUrl, bannerUrl, description, nextpage;
public List<StreamItem> relatedStreams;
public Channel(String name, String avatarUrl, String bannerUrl, String description, String nextpage,
List<StreamItem> relatedStreams) {
this.name = name;
this.avatarUrl = avatarUrl;
this.bannerUrl = bannerUrl;
this.description = description;
this.nextpage = nextpage;
this.relatedStreams = relatedStreams;
public Channel(String id, String name, String avatarUrl, String bannerUrl, String description, String nextpage,
List<StreamItem> relatedStreams) {
this.id = id;
this.name = name;
this.avatarUrl = avatarUrl;
this.bannerUrl = bannerUrl;
this.description = description;
this.nextpage = nextpage;
this.relatedStreams = relatedStreams;
}
}

View File

@ -0,0 +1,12 @@
package me.kavin.piped.utils.obj;
public class ChapterSegment {
public String title;
public int start;
public ChapterSegment(String title, int start) {
this.title = title;
this.start = start;
}
}

View File

@ -2,14 +2,49 @@ package me.kavin.piped.utils.obj;
public class PipedStream {
public String url, format, quality, mimeType;
public String url, format, quality, mimeType, codec;
public boolean videoOnly;
public int bitrate, initStart, initEnd, indexStart, indexEnd, width, height, fps;
public PipedStream(String url, String format, String quality, String mimeType, boolean videoOnly) {
this.url = url;
this.format = format;
this.quality = quality;
this.mimeType = mimeType;
this.videoOnly = videoOnly;
this.url = url;
this.format = format;
this.quality = quality;
this.mimeType = mimeType;
this.videoOnly = videoOnly;
}
public PipedStream(String url, String format, String quality, String mimeType, boolean videoOnly, int bitrate,
int initStart, int initEnd, int indexStart, int indexEnd, String codec) {
this.url = url;
this.format = format;
this.quality = quality;
this.mimeType = mimeType;
this.videoOnly = videoOnly;
this.bitrate = bitrate;
this.initStart = initStart;
this.initEnd = initEnd;
this.indexStart = indexStart;
this.indexEnd = indexEnd;
this.codec = codec;
}
public PipedStream(String url, String format, String quality, String mimeType, boolean videoOnly, int bitrate,
int initStart, int initEnd, int indexStart, int indexEnd, String codec, int width, int height, int fps) {
this.url = url;
this.format = format;
this.quality = quality;
this.mimeType = mimeType;
this.videoOnly = videoOnly;
this.bitrate = bitrate;
this.initStart = initStart;
this.initEnd = initEnd;
this.indexStart = indexStart;
this.indexEnd = indexEnd;
this.codec = codec;
this.width = width;
this.height = height;
this.fps = fps;
}
}

View File

@ -9,15 +9,15 @@ public class Playlist {
public List<StreamItem> relatedStreams;
public Playlist(String name, String thumbnailUrl, String bannerUrl, String nextpage, String uploader,
String uploaderUrl, String uploaderAvatar, int videos, List<StreamItem> relatedStreams) {
this.name = name;
this.thumbnailUrl = thumbnailUrl;
this.bannerUrl = bannerUrl;
this.nextpage = nextpage;
this.videos = videos;
this.uploader = uploader;
this.uploaderUrl = uploaderUrl;
this.uploaderAvatar = uploaderAvatar;
this.relatedStreams = relatedStreams;
String uploaderUrl, String uploaderAvatar, int videos, List<StreamItem> relatedStreams) {
this.name = name;
this.thumbnailUrl = thumbnailUrl;
this.bannerUrl = bannerUrl;
this.nextpage = nextpage;
this.videos = videos;
this.uploader = uploader;
this.uploaderUrl = uploaderUrl;
this.uploaderAvatar = uploaderAvatar;
this.relatedStreams = relatedStreams;
}
}

View File

@ -9,8 +9,8 @@ public class SearchResults {
public ObjectArrayList<SearchItem> items;
public SearchResults(String nextpage, String id, ObjectArrayList<SearchItem> items) {
this.nextpage = nextpage;
this.id = id;
this.items = items;
this.nextpage = nextpage;
this.id = id;
this.items = items;
}
}

View File

@ -6,14 +6,14 @@ public class StreamItem {
public long duration, views;
public StreamItem(String url, String title, String thumbnail, String uploaderName, String uploaderUrl,
String uploadedDate, long duration, long views) {
this.url = url;
this.title = title;
this.thumbnail = thumbnail;
this.uploaderName = uploaderName;
this.uploaderUrl = uploaderUrl;
this.uploadedDate = uploadedDate;
this.duration = duration;
this.views = views;
String uploadedDate, long duration, long views) {
this.url = url;
this.title = title;
this.thumbnail = thumbnail;
this.uploaderName = uploaderName;
this.uploaderUrl = uploaderUrl;
this.uploadedDate = uploadedDate;
this.duration = duration;
this.views = views;
}
}

View File

@ -17,25 +17,25 @@ public class Streams {
public boolean livestream;
public Streams(String title, String description, String uploadDate, String uploader, String uploaderUrl,
String uploaderAvatar, String thumbnailUrl, long duration, long views, long likes, long dislikes,
List<PipedStream> audioStreams, List<PipedStream> videoStreams, List<StreamItem> relatedStreams,
List<Subtitle> subtitles, boolean livestream, String hls) {
this.title = title;
this.description = description;
this.uploadDate = uploadDate;
this.uploader = uploader;
this.uploaderUrl = uploaderUrl;
this.uploaderAvatar = uploaderAvatar;
this.thumbnailUrl = thumbnailUrl;
this.duration = duration;
this.views = views;
this.likes = likes;
this.dislikes = dislikes;
this.audioStreams = audioStreams;
this.videoStreams = videoStreams;
this.relatedStreams = relatedStreams;
this.subtitles = subtitles;
this.livestream = livestream;
this.hls = hls;
String uploaderAvatar, String thumbnailUrl, long duration, long views, long likes, long dislikes,
List<PipedStream> audioStreams, List<PipedStream> videoStreams, List<StreamItem> relatedStreams,
List<Subtitle> subtitles, boolean livestream, String hls) {
this.title = title;
this.description = description;
this.uploadDate = uploadDate;
this.uploader = uploader;
this.uploaderUrl = uploaderUrl;
this.uploaderAvatar = uploaderAvatar;
this.thumbnailUrl = thumbnailUrl;
this.duration = duration;
this.views = views;
this.likes = likes;
this.dislikes = dislikes;
this.audioStreams = audioStreams;
this.videoStreams = videoStreams;
this.relatedStreams = relatedStreams;
this.subtitles = subtitles;
this.livestream = livestream;
this.hls = hls;
}
}

View File

@ -8,7 +8,7 @@ public class StreamsPage {
public List<StreamItem> relatedStreams;
public StreamsPage(String nextpage, List<StreamItem> relatedStreams) {
this.nextpage = nextpage;
this.relatedStreams = relatedStreams;
this.nextpage = nextpage;
this.relatedStreams = relatedStreams;
}
}

View File

@ -5,7 +5,7 @@ public class Subtitle {
public final String url, mimeType;
public Subtitle(String url, String mimeType) {
this.url = url;
this.mimeType = mimeType;
this.url = url;
this.mimeType = mimeType;
};
}

View File

@ -5,20 +5,20 @@ 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;
this.name = name;
this.thumbnail = thumbnail;
this.url = url;
}
public String getName() {
return name;
return name;
}
public String getThumbnail() {
return thumbnail;
return thumbnail;
}
public String getUrl() {
return url;
return url;
}
}

View File

@ -5,16 +5,16 @@ public class SearchStream extends SearchItem {
private long views, duration;
public SearchStream(String name, String thumbnail, String url, long views, long duration) {
super(name, thumbnail, url);
this.views = views;
this.duration = duration;
super(name, thumbnail, url);
this.views = views;
this.duration = duration;
}
public long getViews() {
return views;
return views;
}
public long getDuration() {
return duration;
return duration;
}
}