Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Fijxu 2025-02-23 16:54:01 -03:00
commit 24eafee3e9
Signed by: Fijxu
GPG key ID: 32C1DDF333EDA6A4
10 changed files with 94 additions and 53 deletions

View file

@ -48,3 +48,5 @@ WORKDIR /app
USER appuser USER appuser
ENTRYPOINT ["/tini", "--", "/app/invidious_companion"] ENTRYPOINT ["/tini", "--", "/app/invidious_companion"]
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=3 CMD ["/tini", "--", "/app/invidious_companion", "healthcheck"]

View file

@ -19,6 +19,11 @@ proxy_file = "proxies.txt"
# Enable YouTube new video format UMP # Enable YouTube new video format UMP
ump = false ump = false
# external_videoplayback_proxy = "" # external_videoplayback_proxy = ""
# fetch_timeout_ms = 10000
# fetch_retry_enable = true
# fetch_retry_times = 3
# fetch_retry_initial_debounce = 500
# fetch_retry_debounce_multiplier = 2
[jobs] [jobs]

View file

@ -8,9 +8,9 @@
"hono/logger": "jsr:@hono/hono@^4.6.5/logger", "hono/logger": "jsr:@hono/hono@^4.6.5/logger",
"hono/bearer-auth": "jsr:@hono/hono@^4.6.5/bearer-auth", "hono/bearer-auth": "jsr:@hono/hono@^4.6.5/bearer-auth",
"prom-client": "npm:prom-client@^15.1.3", "prom-client": "npm:prom-client@^15.1.3",
"youtubei.js": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno.ts", "youtubei.js": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.1.0-deno/deno.ts",
"youtubei.js/Utils": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/utils/Utils.ts", "youtubei.js/Utils": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.1.0-deno/deno/src/utils/Utils.ts",
"youtubei.js/NavigationEndpoint": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/parser/classes/NavigationEndpoint.ts", "youtubei.js/NavigationEndpoint": "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.1.0-deno/deno/src/parser/classes/NavigationEndpoint.ts",
"jsdom": "npm:jsdom@25.0.1", "jsdom": "npm:jsdom@25.0.1",
"bgutils": "https://esm.sh/bgutils-js@3.1.0", "bgutils": "https://esm.sh/bgutils-js@3.1.0",
"estree": "https://esm.sh/@types/estree@1.0.6", "estree": "https://esm.sh/@types/estree@1.0.6",

View file

@ -1,4 +1,11 @@
import { Store } from "@willsoto/node-konfig-core"; import { Store } from "@willsoto/node-konfig-core";
import { retry, type RetryOptions } from "jsr:@std/async";
type FetchInputParameter = Parameters<typeof fetch>[0];
type FetchInitParameterWithClient =
| RequestInit
| RequestInit & { client: Deno.HttpClient };
type FetchReturn = ReturnType<typeof fetch>;
let proxies: string[] = []; let proxies: string[] = [];
let currentProxyIndex = 0; let currentProxyIndex = 0;
@ -15,21 +22,17 @@ try {
} }
export const getFetchClient = (konfigStore: Store): { export const getFetchClient = (konfigStore: Store): {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
( (
input: Request | URL | string, input: FetchInputParameter,
init?: RequestInit & { init?: FetchInitParameterWithClient,
client: Deno.HttpClient; ): FetchReturn;
},
): Promise<Response>;
(input: URL | Request | string, init?: RequestInit): Promise<Response>;
} => { } => {
if ( if (
Deno.env.get("PROXY") || konfigStore.get("networking.proxy") || Deno.env.get("PROXY") || konfigStore.get("networking.proxy") ||
(proxies.length > 0) (proxies.length > 0)
) { ) {
return async ( return async (
input: RequestInfo | URL, input: FetchInputParameter,
init?: RequestInit, init?: RequestInit,
) => { ) => {
const proxyUrl = Deno.env.get("PROXY") || const proxyUrl = Deno.env.get("PROXY") ||
@ -60,5 +63,39 @@ export const getFetchClient = (konfigStore: Store): {
}; };
} }
return globalThis.fetch; return (input: FetchInputParameter, init?: FetchInitParameterWithClient) =>
fetchShim(konfigStore, input, init);
}; };
function fetchShim(
konfigStore: Store,
input: FetchInputParameter,
init?: FetchInitParameterWithClient,
): FetchReturn {
const fetchTimeout = konfigStore.get("networking.fetch_timeout_ms");
const fetchRetry = konfigStore.get("networking.fetch_retry_enable");
const fetchMaxAttempts = konfigStore.get("networking.fetch_retry_times");
const fetchInitialDebounce = konfigStore.get(
"networking.fetch_retry_initial_debounce",
);
const fetchDebounceMultiplier = konfigStore.get(
"networking.fetch_retry_debounce_multiplier",
);
const retryOptions: RetryOptions = {
maxAttempts: Number(fetchMaxAttempts) || 1,
minTimeout: Number(fetchInitialDebounce) || 0,
multiplier: Number(fetchDebounceMultiplier) || 0,
jitter: 0,
};
const callFetch = () =>
fetch(input, {
// only set the AbortSignal if the timeout is supplied in the config
signal: fetchTimeout
? AbortSignal.timeout(Number(fetchTimeout))
: null,
...(init || {}),
});
// if retry enabled, call retry with the fetch shim, otherwise pass the fetch shim back directly
return fetchRetry ? retry(callFetch, retryOptions) : callFetch();
}

View file

@ -1,5 +1,7 @@
import { Innertube } from "youtubei.js"; import { Innertube } from "youtubei.js";
import type { konfigLoader } from "../helpers/konfigLoader.ts";
export type HonoVariables = { export type HonoVariables = {
innertubeClient: Innertube; innertubeClient: Innertube;
konfigStore: Awaited<ReturnType<typeof konfigLoader>>;
}; };

View file

@ -13,6 +13,7 @@ import { konfigLoader } from "./lib/helpers/konfigLoader.ts";
import { ICache } from "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/types/Cache.ts"; import { ICache } from "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/types/Cache.ts";
import { FetchFunction } from "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/types/PlatformShim.ts"; import { FetchFunction } from "https://raw.githubusercontent.com/LuanRT/YouTube.js/refs/tags/v13.0.0-deno/deno/src/types/PlatformShim.ts";
import { getRandomUserAgent, PlayerError } from "youtubei.js/Utils"; import { getRandomUserAgent, PlayerError } from "youtubei.js/Utils";
import type { HonoVariables } from "./lib/types/HonoVariables.ts";
let getFetchClientLocation = "getFetchClient"; let getFetchClientLocation = "getFetchClient";
if (Deno.env.get("GET_FETCH_CLIENT_LOCATION")) { if (Deno.env.get("GET_FETCH_CLIENT_LOCATION")) {
if (Deno.env.has("DENO_COMPILED")) { if (Deno.env.has("DENO_COMPILED")) {
@ -24,10 +25,31 @@ if (Deno.env.get("GET_FETCH_CLIENT_LOCATION")) {
) as string; ) as string;
} }
} }
const args = Deno.args;
const konfigStore = await konfigLoader();
const host = Deno.env.get("HOST") || konfigStore.get("server.host") as string;
const port = Deno.env.get("PORT") || konfigStore.get("server.port") as string;
if (args?.[0] == "healthcheck") {
try {
const response = await fetch(`http://${host}:${port}/healthz`);
if (response.status === 200) {
Deno.exit(0);
} else {
Deno.exit(1);
}
} catch (_) {
Deno.exit(1);
}
}
const { getFetchClient } = await import(getFetchClientLocation); const { getFetchClient } = await import(getFetchClientLocation);
declare module "hono" {
interface ContextVariableMap extends HonoVariables {}
}
const app = new Hono(); const app = new Hono();
const konfigStore = await konfigLoader();
let innertubeClient: Innertube; let innertubeClient: Innertube;
let innertubeClientFetchPlayer = true; let innertubeClientFetchPlayer = true;
@ -215,9 +237,7 @@ if (!innertubeClientOauthEnabled) {
} }
app.use("*", async (c, next) => { app.use("*", async (c, next) => {
// @ts-ignore Do not understand how to fix this error.
c.set("innertubeClient", innertubeClient); c.set("innertubeClient", innertubeClient);
// @ts-ignore Do not understand how to fix this error.
c.set("konfigStore", konfigStore); c.set("konfigStore", konfigStore);
await next(); await next();
}); });
@ -225,8 +245,6 @@ app.use("*", async (c, next) => {
routes(app, konfigStore); routes(app, konfigStore);
const https = Deno.env.get("HTTPS"); const https = Deno.env.get("HTTPS");
const port = konfigStore.get("server.port") as number;
const host = konfigStore.get("server.host") as string;
if (https == "TRUE" || https == "true") { if (https == "TRUE" || https == "true") {
const cert = Deno.readTextFileSync("/data/cert.pem"); const cert = Deno.readTextFileSync("/data/cert.pem");

View file

@ -1,7 +1,5 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { FormatUtils, Innertube } from "youtubei.js"; import { FormatUtils } from "youtubei.js";
import { HonoVariables } from "../../lib/types/HonoVariables.ts";
import { Store } from "@willsoto/node-konfig-core";
import { import {
youtubePlayerParsing, youtubePlayerParsing,
youtubeVideoInfo, youtubeVideoInfo,
@ -9,18 +7,15 @@ import {
import { verifyRequest } from "../../lib/helpers/verifyRequest.ts"; import { verifyRequest } from "../../lib/helpers/verifyRequest.ts";
import { HTTPException } from "hono/http-exception"; import { HTTPException } from "hono/http-exception";
const dashManifest = new Hono<{ Variables: HonoVariables }>(); const dashManifest = new Hono();
dashManifest.get("/:videoId", async (c) => { dashManifest.get("/:videoId", async (c) => {
const { videoId } = c.req.param(); const { videoId } = c.req.param();
const { check, local } = c.req.query(); const { check, local } = c.req.query();
c.header("access-control-allow-origin", "*"); c.header("access-control-allow-origin", "*");
const innertubeClient = await c.get("innertubeClient") as Innertube; const innertubeClient = c.get("innertubeClient");
// @ts-ignore Do not understand how to fix this error. const konfigStore = c.get("konfigStore");
const konfigStore = await c.get("konfigStore") as Store<
Record<string, unknown>
>;
const maxDashResolution = Deno.env.get("SERVER_MAX_DASH_RESOLUTION") || const maxDashResolution = Deno.env.get("SERVER_MAX_DASH_RESOLUTION") ||
konfigStore.get("server.max_dash_resolution") as number; konfigStore.get("server.max_dash_resolution") as number;

View file

@ -1,15 +1,12 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { HTTPException } from "hono/http-exception"; import { HTTPException } from "hono/http-exception";
import { Innertube } from "youtubei.js";
import { HonoVariables } from "../../lib/types/HonoVariables.ts";
import { Store } from "@willsoto/node-konfig-core";
import { import {
youtubePlayerParsing, youtubePlayerParsing,
youtubeVideoInfo, youtubeVideoInfo,
} from "../../lib/helpers/youtubePlayerHandling.ts"; } from "../../lib/helpers/youtubePlayerHandling.ts";
import { verifyRequest } from "../../lib/helpers/verifyRequest.ts"; import { verifyRequest } from "../../lib/helpers/verifyRequest.ts";
const latestVersion = new Hono<{ Variables: HonoVariables }>(); const latestVersion = new Hono();
latestVersion.get("/", async (c) => { latestVersion.get("/", async (c) => {
const { check, itag, id, local } = c.req.query(); const { check, itag, id, local } = c.req.query();
@ -21,19 +18,14 @@ latestVersion.get("/", async (c) => {
}); });
} }
const innertubeClient = await c.get("innertubeClient") as Innertube; const innertubeClient = c.get("innertubeClient");
// @ts-ignore Do not understand how to fix this error. const konfigStore = await c.get("konfigStore");
const konfigStore = await c.get("konfigStore") as Store<
Record<string, unknown>
>;
const verifyRequests = Deno.env.get("VERIFY_REQUESTS") ||
konfigStore.get("server.verify_requests");
if (verifyRequests && check == undefined) { if (konfigStore.get("server.verify_requests") && check == undefined) {
throw new HTTPException(400, { throw new HTTPException(400, {
res: new Response("No check ID."), res: new Response("No check ID."),
}); });
} else if (verifyRequests && check) { } else if (konfigStore.get("server.verify_requests") && check) {
if (verifyRequest(check, id, konfigStore) === false) { if (verifyRequest(check, id, konfigStore) === false) {
throw new HTTPException(400, { throw new HTTPException(400, {
res: new Response("ID incorrect."), res: new Response("ID incorrect."),

View file

@ -1,5 +1,4 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { Store } from "@willsoto/node-konfig-core";
import { HTTPException } from "hono/http-exception"; import { HTTPException } from "hono/http-exception";
let getFetchClientLocation = "getFetchClient"; let getFetchClientLocation = "getFetchClient";
if (Deno.env.get("GET_FETCH_CLIENT_LOCATION")) { if (Deno.env.get("GET_FETCH_CLIENT_LOCATION")) {
@ -44,10 +43,7 @@ videoPlaybackProxy.get("/", async (c) => {
}); });
} }
// @ts-ignore Do not understand how to fix this error. const konfigStore = c.get("konfigStore");
const konfigStore = await c.get("konfigStore") as Store<
Record<string, unknown>
>;
// deno-lint-ignore prefer-const // deno-lint-ignore prefer-const
let queryParams = new URLSearchParams(urlReq.search); let queryParams = new URLSearchParams(urlReq.search);

View file

@ -1,8 +1,5 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { youtubePlayerParsing } from "../../lib/helpers/youtubePlayerHandling.ts"; import { youtubePlayerParsing } from "../../lib/helpers/youtubePlayerHandling.ts";
import { HonoVariables } from "../../lib/types/HonoVariables.ts";
import { Innertube } from "youtubei.js";
import { Store } from "@willsoto/node-konfig-core";
import { import {
reasonBot, reasonBot,
subreasonProtectCommunity, subreasonProtectCommunity,
@ -10,7 +7,7 @@ import {
videoUnavailable, videoUnavailable,
} from "metrics"; } from "metrics";
const player = new Hono<{ Variables: HonoVariables }>(); const player = new Hono();
const errors = [ const errors = [
{ {
@ -47,11 +44,8 @@ const errors = [
player.post("/player", async (c) => { player.post("/player", async (c) => {
const jsonReq = await c.req.json(); const jsonReq = await c.req.json();
const innertubeClient = await c.get("innertubeClient") as Innertube; const innertubeClient = c.get("innertubeClient");
// @ts-ignore Do not understand how to fix this error. const konfigStore = c.get("konfigStore");
const konfigStore = await c.get("konfigStore") as Store<
Record<string, unknown>
>;
if (jsonReq.videoId) { if (jsonReq.videoId) {
const yt = await youtubePlayerParsing( const yt = await youtubePlayerParsing(
innertubeClient, innertubeClient,