This commit is contained in:
Venkatesan Ravi
2025-10-31 21:09:08 -07:00
parent 401df6c721
commit 06fc3666ab
9 changed files with 122 additions and 91 deletions

View File

@@ -2,7 +2,7 @@ import { build } from "esbuild";
async function main() {
await build({
entryPoints: ["./src/cli.ts"],
entryPoints: ["./src/cli/index.ts"],
bundle: true,
sourcemap: "inline",
platform: "node",

View File

@@ -25,6 +25,16 @@ export default defineConfig(
variableDeclaration: true,
},
],
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: false,
allowTypedFunctionExpressions: true,
allowHigherOrderFunctions: true,
allowDirectConstAssertionInArrowFunctions: true,
},
],
},
},
[globalIgnores(["**bundle.cjs", "esbuild.ts", "eslint.config.mjs"])],

View File

@@ -1,7 +1,10 @@
import createClient from "openapi-fetch";
import type { paths } from "../generated/schema";
import type { paths } from "../../generated/schema";
export function makeClient(baseUrl: string, apiKey: string) {
export function makeClient(
baseUrl: string,
apiKey: string,
): ReturnType<typeof createClient<paths>> {
const client: ReturnType<typeof createClient<paths>> = createClient<paths>({
baseUrl: baseUrl.replace(/\/+$/, ""),
});

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
import { Command } from "commander";
import { runPipeline } from "./pipeline";
import { runPipeline } from "../pipeline";
interface Options {
config: string;

View File

@@ -0,0 +1,32 @@
import type { components } from "../../../generated/schema";
export type ServerConfiguration = components["schemas"]["ServerConfiguration"];
export type JFPluginRepo = NonNullable<
ServerConfiguration["PluginRepositories"]
>[number];
export type JFTrickplay = NonNullable<ServerConfiguration["TrickplayOptions"]>;
export interface PluginRepositoryCfg {
name: string;
url: string;
enabled: boolean;
}
export interface TrickplayOptionsCfg {
enableHwAcceleration?: boolean | null;
enableHwEncoding?: boolean | null;
}
export interface SystemCfg {
enableMetrics?: boolean;
pluginRepositories?: PluginRepositoryCfg[];
trickplayOptions?: TrickplayOptionsCfg;
}
export interface RootConfig {
version: number;
base_url: string;
system: SystemCfg;
}

48
src/mappers/system.ts Normal file
View File

@@ -0,0 +1,48 @@
import type { JFPluginRepo, PluginRepositoryCfg } from "../domain/system/types";
export function fromJFRepos(
inRepos: JFPluginRepo[] | undefined,
): PluginRepositoryCfg[] {
if (!inRepos) return [];
return inRepos.map(
(r: JFPluginRepo): PluginRepositoryCfg => ({
name: r.Name ?? "",
url: r.Url ?? "",
enabled: Boolean(r.Enabled),
}),
);
}
export function toJFRepos(inRepos: PluginRepositoryCfg[]): JFPluginRepo[] {
return inRepos.map(
(r: PluginRepositoryCfg): JFPluginRepo => ({
Name: r.name,
Url: r.url,
Enabled: r.enabled,
}),
);
}
export function equalReposUnordered(
a: PluginRepositoryCfg[],
b: PluginRepositoryCfg[],
): boolean {
if (a.length !== b.length) return false;
const key: (r: PluginRepositoryCfg) => string = (
r: PluginRepositoryCfg,
): string => `${r.name}::${r.url}`;
const am: Map<string, PluginRepositoryCfg> = new Map(
a.map((r: PluginRepositoryCfg): [string, PluginRepositoryCfg] => [
key(r),
r,
]),
);
for (const r of b) {
const other: PluginRepositoryCfg | undefined = am.get(key(r));
if (!other || other.enabled !== r.enabled) return false;
}
return true;
}

View File

@@ -1,23 +1,16 @@
import { promises as fs } from "fs";
import YAML from "yaml";
import createClient from "openapi-fetch";
import { makeClient } from "./client";
import type { paths } from "../generated/schema";
import type { components } from "../generated/schema";
import { apply, type SystemCfg } from "./system";
import type createClient from "openapi-fetch";
import type { paths } from "../../generated/schema";
import { makeClient } from "../api/client";
import type { RootConfig, ServerConfiguration } from "../domain/system/types";
import { apply } from "../services/system/apply";
type JFClient = ReturnType<typeof createClient<paths>>;
type ServerConfiguration = components["schemas"]["ServerConfiguration"];
interface Config {
version: number;
base_url: string;
system: SystemCfg;
}
export async function runPipeline(path: string): Promise<void> {
const raw: string = await fs.readFile(path, "utf8");
const cfg: Config = YAML.parse(raw) as Config;
const cfg: RootConfig = YAML.parse(raw) as RootConfig;
const apiKey: string | undefined = process.env.JELLYFIN_API_KEY;
if (!apiKey) throw new Error("JELLYFIN_API_KEY required");

View File

@@ -1,75 +1,16 @@
import { logger } from "./logger";
import type { components } from "../generated/schema";
type ServerConfiguration = components["schemas"]["ServerConfiguration"];
type JFPluginRepo = NonNullable<
ServerConfiguration["PluginRepositories"]
>[number];
type JFTrickplay = NonNullable<ServerConfiguration["TrickplayOptions"]>;
export interface PluginRepositoryCfg {
name: string;
url: string;
enabled: boolean;
}
export interface TrickplayOptionsCfg {
enableHwAcceleration?: boolean | null;
enableHwEncoding?: boolean | null;
}
export interface SystemCfg {
enableMetrics?: boolean;
pluginRepositories?: PluginRepositoryCfg[];
trickplayOptions?: TrickplayOptionsCfg;
}
function fromJFRepos(
inRepos: JFPluginRepo[] | undefined,
): PluginRepositoryCfg[] {
if (!inRepos) return [];
return inRepos.map(
(r: JFPluginRepo): PluginRepositoryCfg => ({
name: r.Name ?? "",
url: r.Url ?? "",
enabled: Boolean(r.Enabled),
}),
);
}
function toJFRepos(inRepos: PluginRepositoryCfg[]): JFPluginRepo[] {
return inRepos.map(
(r: PluginRepositoryCfg): JFPluginRepo => ({
Name: r.name,
Url: r.url,
Enabled: r.enabled,
}),
);
}
function equalReposUnordered(
a: PluginRepositoryCfg[],
b: PluginRepositoryCfg[],
): boolean {
if (a.length !== b.length) return false;
const key: (r: PluginRepositoryCfg) => string = (
r: PluginRepositoryCfg,
): string => `${r.name}::${r.url}`;
const am: Map<string, PluginRepositoryCfg> = new Map(
a.map((r: PluginRepositoryCfg): [string, PluginRepositoryCfg] => [
key(r),
r,
]),
);
for (const r of b) {
const other: PluginRepositoryCfg | undefined = am.get(key(r));
if (!other || other.enabled !== r.enabled) return false;
}
return true;
}
import { logger } from "../../lib/logger";
import type {
JFTrickplay,
PluginRepositoryCfg,
ServerConfiguration,
SystemCfg,
TrickplayOptionsCfg,
} from "../../domain/system/types";
import {
equalReposUnordered,
fromJFRepos,
toJFRepos,
} from "../../mappers/system";
function hasEnableMetricsChanged(
current: ServerConfiguration,
@@ -161,10 +102,14 @@ export function apply(
const next: JFTrickplay = { ...cur };
if ("enableHwAcceleration" in cfg) {
next.EnableHwAcceleration = cfg.enableHwAcceleration ?? null;
const v: boolean | null | undefined = cfg.enableHwAcceleration;
const vOut: boolean | undefined = v === null ? undefined : v;
next.EnableHwAcceleration = vOut;
}
if ("enableHwEncoding" in cfg) {
next.EnableHwEncoding = cfg.enableHwEncoding ?? null;
const v: boolean | null | undefined = cfg.enableHwEncoding;
const vOut: boolean | undefined = v === null ? undefined : v;
next.EnableHwEncoding = vOut;
}
out.TrickplayOptions = next;