allow to set specific directory for cache + various envs

This commit is contained in:
Emilien 2024-11-01 21:00:30 +01:00
parent fa2fda8b72
commit be1f4bee39
11 changed files with 135 additions and 22 deletions

View file

@ -0,0 +1,72 @@
name: Build and Push Docker Image
# Define when this workflow will run
on:
push:
branches:
- master # Trigger on pushes to master branch
tags:
- '[0-9]+.[0-9]+.[0-9]+' # Trigger on semantic version tags
paths-ignore:
- 'Cargo.lock'
- 'LICENSE'
- 'README.md'
- 'docker-compose.yml'
workflow_dispatch: # Allow manual triggering of the workflow
# Define environment variables used throughout the workflow
env:
REGISTRY: quay.io
IMAGE_NAME: invidious/invidious-companion
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
# Step 1: Check out the repository code
- name: Checkout code
uses: actions/checkout@v3
# Step 2: Set up QEMU for multi-architecture builds
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
# Step 3: Set up Docker Buildx for enhanced build capabilities
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Step 4: Authenticate with Quay.io registry
- name: Log in to Quay.io
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
# Step 5: Extract metadata for Docker image tagging and labeling
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Define tagging strategy
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
type=sha,prefix={{branch}}-
# Define labels
labels: |
quay.expires-after=12w
# Step 6: Build and push the Docker image
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64 # Build for multiple architectures
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View file

@ -26,6 +26,9 @@ COPY --from=builder /app/invidious_companion /app/
COPY ./config/ /app/config/ COPY ./config/ /app/config/
COPY --from=builder /tini /tini COPY --from=builder /tini /tini
ENV PORT=8282 \
HOST=0.0.0.0
# Copy passwd file for the non-privileged user from the user-stage # Copy passwd file for the non-privileged user from the user-stage
COPY --from=user-stage /etc/passwd /etc/passwd COPY --from=user-stage /etc/passwd /etc/passwd

View file

@ -6,6 +6,9 @@ base_url = "http://localhost:8282"
[cache] [cache]
enabled = true enabled = true
# will get cached in /var/tmp/youtubei.js if you specify /var/tmp
# you need to change the --allow-write from deno run too
directory = "/var/tmp"
[networking] [networking]
#proxy = "" #proxy = ""

View file

@ -1,7 +1,7 @@
{ {
"tasks": { "tasks": {
"dev": "deno run --allow-net --allow-env --allow-read --allow-sys=hostname --allow-write=./,/tmp/youtubei.js --watch src/main.ts", "dev": "deno run --allow-net --allow-env --allow-sys=hostname --allow-write=/var/tmp/youtubei.js --watch src/main.ts",
"compile": "deno compile --output invidious_companion --allow-net --allow-env --allow-sys=hostname --allow-read src/main.ts" "compile": "deno compile --output invidious_companion --allow-net --allow-env --allow-read --allow-sys=hostname --allow-write=/var/tmp/youtubei.js src/main.ts"
}, },
"imports": { "imports": {
"hono": "jsr:@hono/hono@^4.6.5", "hono": "jsr:@hono/hono@^4.6.5",
@ -9,9 +9,10 @@
"hono/bearer-auth": "jsr:@hono/hono@^4.6.5/bearer-auth", "hono/bearer-auth": "jsr:@hono/hono@^4.6.5/bearer-auth",
"youtubei.js": "https://deno.land/x/youtubei@v11.0.1-deno/deno.ts", "youtubei.js": "https://deno.land/x/youtubei@v11.0.1-deno/deno.ts",
"youtubei.js/endpoints": "https://deno.land/x/youtubei@v11.0.1-deno/deno/src/core/endpoints/index.ts", "youtubei.js/endpoints": "https://deno.land/x/youtubei@v11.0.1-deno/deno/src/core/endpoints/index.ts",
"youtubei.js/Utils": "https://deno.land/x/youtubei@v11.0.1-deno/deno/src/utils/Utils.ts",
"jsdom": "https://esm.sh/jsdom@25.0.1", "jsdom": "https://esm.sh/jsdom@25.0.1",
"bgutils": "https://esm.sh/bgutils-js@3.0.0", "bgutils": "https://esm.sh/bgutils-js@3.0.0",
"estree": "npm:@types/estree", "estree": "https://esm.sh/@types/estree@1.0.6",
"@willsoto/node-konfig-core": "npm:@willsoto/node-konfig-core@5.0.0", "@willsoto/node-konfig-core": "npm:@willsoto/node-konfig-core@5.0.0",
"@willsoto/node-konfig-file": "npm:@willsoto/node-konfig-file@3.0.0", "@willsoto/node-konfig-file": "npm:@willsoto/node-konfig-file@3.0.0",
"@willsoto/node-konfig-toml-parser": "npm:@willsoto/node-konfig-toml-parser@3.0.0", "@willsoto/node-konfig-toml-parser": "npm:@willsoto/node-konfig-toml-parser@3.0.0",

View file

@ -740,7 +740,6 @@
"workspace": { "workspace": {
"dependencies": [ "dependencies": [
"jsr:@hono/hono@^4.6.5", "jsr:@hono/hono@^4.6.5",
"npm:@types/estree",
"npm:@willsoto/node-konfig-core@5.0.0", "npm:@willsoto/node-konfig-core@5.0.0",
"npm:@willsoto/node-konfig-file@3.0.0", "npm:@willsoto/node-konfig-file@3.0.0",
"npm:@willsoto/node-konfig-toml-parser@3.0.0" "npm:@willsoto/node-konfig-toml-parser@3.0.0"

19
docker-compose.yaml Normal file
View file

@ -0,0 +1,19 @@
version: "3"
services:
invidious_companion:
build:
context: .
dockerfile: Dockerfile
# image: quay.io/invidious/invidious-companion:latest
ports:
- 127.0.0.1:8282:8282
restart: unless-stopped
cap_drop:
- ALL
read_only: true
user: 10001:10001
# cache for youtube library
volumes:
- /var/tmp/youtubei.js:/var/tmp/youtubei.js:rw
security_opt:
- no-new-privileges:true

View file

@ -6,14 +6,14 @@ export const getFetchClient = (konfigStore: Store): {
client: Deno.HttpClient; client: Deno.HttpClient;
}): Promise<Response>; }): Promise<Response>;
} => { } => {
if (konfigStore.get("networking.proxy")) { if (Deno.env.get("PROXY") || konfigStore.get("networking.proxy")) {
return async ( return async (
input: RequestInfo | URL, input: RequestInfo | URL,
init?: RequestInit, init?: RequestInit,
) => { ) => {
const client = Deno.createHttpClient({ const client = Deno.createHttpClient({
proxy: { proxy: {
url: konfigStore.get("networking.proxy") as string, url: Deno.env.get("PROXY") || konfigStore.get("networking.proxy") as string,
}, },
}); });
const fetchRes = await fetch(input, { const fetchRes = await fetch(input, {

View file

@ -1,4 +1,5 @@
import { Innertube, YT, ApiResponse } from "youtubei.js"; import { Innertube, YT, ApiResponse } from "youtubei.js";
import { generateRandomString } from "youtubei.js/Utils";
import { compress, decompress } from "https://deno.land/x/brotli@0.1.7/mod.ts"; import { compress, decompress } from "https://deno.land/x/brotli@0.1.7/mod.ts";
import { youtubePlayerReq } from "youtubePlayerReq"; import { youtubePlayerReq } from "youtubePlayerReq";
import { Store } from "@willsoto/node-konfig-core"; import { Store } from "@willsoto/node-konfig-core";
@ -28,7 +29,7 @@ export const youtubePlayerParsing = async (
const video = new YT.VideoInfo( const video = new YT.VideoInfo(
[youtubePlayerResponse], [youtubePlayerResponse],
innertubeClient.actions, innertubeClient.actions,
"", generateRandomString(16),
); );
const streamingData = video.streaming_data; const streamingData = video.streaming_data;
@ -89,7 +90,7 @@ export const youtubePlayerParsing = async (
videoDetails, videoDetails,
microformat, microformat,
invidiousCompanion: { invidiousCompanion: {
"baseUrl": konfigStore.get("server.base_url") as string, "baseUrl": Deno.env.get("SERVER_BASE_URL") || konfigStore.get("server.base_url") as string,
}, },
}))(videoData); }))(videoData);

View file

@ -9,6 +9,7 @@ import { getFetchClient } from "../helpers/getFetchClient.ts";
export const poTokenGenerate = async ( export const poTokenGenerate = async (
innertubeClient: Innertube, innertubeClient: Innertube,
konfigStore: Store<Record<string, unknown>>, konfigStore: Store<Record<string, unknown>>,
innertubeClientCache: UniversalCache
): Promise<Innertube> => { ): Promise<Innertube> => {
const requestKey = "O43z0dpjhgX20SCx4KAo"; const requestKey = "O43z0dpjhgX20SCx4KAo";
@ -62,7 +63,7 @@ export const poTokenGenerate = async (
po_token: poTokenResult.poToken, po_token: poTokenResult.poToken,
visitor_data: visitorData, visitor_data: visitorData,
fetch: getFetchClient(konfigStore), fetch: getFetchClient(konfigStore),
cache: new UniversalCache(true), cache: innertubeClientCache,
generate_session_locally: true, generate_session_locally: true,
})); }));
}; };

View file

@ -6,11 +6,9 @@ import { konfigLoader } from "./lib/helpers/konfigLoader.ts";
import { getFetchClient } from "./lib/helpers/getFetchClient.ts"; import { getFetchClient } from "./lib/helpers/getFetchClient.ts";
const app = new Hono(); const app = new Hono();
const konfigStore = await konfigLoader(); const konfigStore = await konfigLoader();
let innertubeClient: Innertube; let innertubeClient: Innertube;
let innertubeClientUniversalCache = true;
let innertubeClientFetchPlayer = true; let innertubeClientFetchPlayer = true;
const innertubeClientOauthEnabled = konfigStore.get( const innertubeClientOauthEnabled = konfigStore.get(
"youtube_session.oauth_enabled", "youtube_session.oauth_enabled",
@ -21,6 +19,12 @@ const innertubeClientJobPoTokenEnabled = konfigStore.get(
const innertubeClientCookies = konfigStore.get( const innertubeClientCookies = konfigStore.get(
"jobs.youtube_session.cookies", "jobs.youtube_session.cookies",
) as string; ) as string;
let innertubeClientCache = new UniversalCache(
true,
konfigStore.get('cache.directory') as string + "/youtubei.js/",
) as UniversalCache;
Deno.env.set('TMPDIR', konfigStore.get("cache.directory") as string)
if (!innertubeClientOauthEnabled) { if (!innertubeClientOauthEnabled) {
if (innertubeClientJobPoTokenEnabled) { if (innertubeClientJobPoTokenEnabled) {
@ -32,29 +36,37 @@ if (!innertubeClientOauthEnabled) {
} }
} else if (innertubeClientOauthEnabled) { } else if (innertubeClientOauthEnabled) {
// Can't use cache if using OAuth#cacheCredentials // Can't use cache if using OAuth#cacheCredentials
innertubeClientUniversalCache = false; innertubeClientCache = new UniversalCache(false);
} }
innertubeClient = await Innertube.create({ innertubeClient = await Innertube.create({
cache: new UniversalCache(innertubeClientUniversalCache), cache: innertubeClientCache,
retrieve_player: innertubeClientFetchPlayer, retrieve_player: innertubeClientFetchPlayer,
fetch: getFetchClient(konfigStore), fetch: getFetchClient(konfigStore),
cookie: innertubeClientCookies || undefined cookie: innertubeClientCookies || undefined,
}); });
if (!innertubeClientOauthEnabled) { if (!innertubeClientOauthEnabled) {
if (innertubeClientOauthEnabled) { if (innertubeClientOauthEnabled) {
innertubeClient = await poTokenGenerate(innertubeClient, konfigStore); innertubeClient = await poTokenGenerate(
innertubeClient,
konfigStore,
innertubeClientCache as UniversalCache,
);
} }
Deno.cron( Deno.cron(
"regenerate youtube session", "regenerate youtube session",
konfigStore.get("jobs.youtube_session.frequency") as string, konfigStore.get("jobs.youtube_session.frequency") as string,
async () => { async () => {
if (innertubeClientOauthEnabled) { if (innertubeClientOauthEnabled) {
innertubeClient = await poTokenGenerate(innertubeClient, konfigStore); innertubeClient = await poTokenGenerate(
innertubeClient,
konfigStore,
innertubeClientCache,
);
} else { } else {
innertubeClient = await Innertube.create({ innertubeClient = await Innertube.create({
cache: new UniversalCache(innertubeClientUniversalCache), cache: innertubeClientCache,
retrieve_player: innertubeClientFetchPlayer, retrieve_player: innertubeClientFetchPlayer,
}); });
} }
@ -62,8 +74,10 @@ if (!innertubeClientOauthEnabled) {
); );
} else if (innertubeClientOauthEnabled) { } else if (innertubeClientOauthEnabled) {
// Fired when waiting for the user to authorize the sign in attempt. // Fired when waiting for the user to authorize the sign in attempt.
innertubeClient.session.on('auth-pending', (data) => { innertubeClient.session.on("auth-pending", (data) => {
console.log(`Go to ${data.verification_url} in your browser and enter code ${data.user_code} to authenticate.`); console.log(
`Go to ${data.verification_url} in your browser and enter code ${data.user_code} to authenticate.`,
);
}); });
// Fired when authentication is successful. // Fired when authentication is successful.
innertubeClient.session.on("auth", () => { innertubeClient.session.on("auth", () => {
@ -91,6 +105,6 @@ app.use("*", async (c, next) => {
routes(app, konfigStore); routes(app, konfigStore);
Deno.serve({ Deno.serve({
port: konfigStore.get("server.port") as number, port: Number(Deno.env.get("PORT")) || konfigStore.get("server.port") as number,
hostname: konfigStore.get("server.host") as string, hostname: Deno.env.get("HOST") || konfigStore.get("server.host") as string,
}, app.fetch); }, app.fetch);

View file

@ -13,7 +13,7 @@ export const routes = (app: Hono, konfigStore: Store<Record<string, unknown>>) =
app.use( app.use(
"/youtubei/v1/*", "/youtubei/v1/*",
bearerAuth({ bearerAuth({
token: konfigStore.get("server.hmac_key") as string, token: Deno.env.get("SERVER_HMAC_KEY") || konfigStore.get("server.hmac_key") as string,
}), }),
); );