@m5kdev/backend 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3279,13 +3279,13 @@ declare function createBetterAuth<O extends Orm, S extends Schema, E extends Ema
3279
3279
  $Infer: {
3280
3280
  body: ({
3281
3281
  permission: {
3282
- readonly user?: ("set-role" | "create" | "update" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3282
+ readonly user?: ("update" | "set-role" | "create" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3283
3283
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3284
3284
  };
3285
3285
  permissions?: never | undefined;
3286
3286
  } | {
3287
3287
  permissions: {
3288
- readonly user?: ("set-role" | "create" | "update" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3288
+ readonly user?: ("update" | "set-role" | "create" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3289
3289
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3290
3290
  };
3291
3291
  permission?: never | undefined;
@@ -3279,13 +3279,13 @@ declare function createBetterAuth<O extends Orm, S extends Schema, E extends Ema
3279
3279
  $Infer: {
3280
3280
  body: ({
3281
3281
  permission: {
3282
- readonly user?: ("set-role" | "create" | "update" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3282
+ readonly user?: ("update" | "set-role" | "create" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3283
3283
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3284
3284
  };
3285
3285
  permissions?: never | undefined;
3286
3286
  } | {
3287
3287
  permissions: {
3288
- readonly user?: ("set-role" | "create" | "update" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3288
+ readonly user?: ("update" | "set-role" | "create" | "delete" | "list" | "get" | "ban" | "impersonate" | "set-password")[] | undefined;
3289
3289
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3290
3290
  };
3291
3291
  permission?: never | undefined;
@@ -4,7 +4,7 @@ import * as _$drizzle_orm_sqlite_core0 from "drizzle-orm/sqlite-core";
4
4
  import { LibSQLDatabase } from "drizzle-orm/libsql";
5
5
  import { InferSelectModel } from "drizzle-orm";
6
6
  import { BillingSchema } from "@m5kdev/commons/modules/billing/billing.schema";
7
- import { Stripe } from "stripe";
7
+ import { Stripe as Stripe$1 } from "stripe";
8
8
  import { StripePlan } from "@m5kdev/commons/modules/billing/billing.types";
9
9
 
10
10
  //#region src/modules/billing/billing.repository.d.ts
@@ -2713,7 +2713,7 @@ declare const schema: {
2713
2713
  type Schema = typeof schema;
2714
2714
  type Orm = LibSQLDatabase<Schema>;
2715
2715
  declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<string, never>, Schema["subscriptions"]> {
2716
- stripe: Stripe;
2716
+ stripe: Stripe$1;
2717
2717
  plans: StripePlan[];
2718
2718
  trial?: StripePlan;
2719
2719
  constructor(options: {
@@ -2721,7 +2721,7 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2721
2721
  schema: Schema;
2722
2722
  table: Schema["subscriptions"];
2723
2723
  libs: {
2724
- stripe: Stripe;
2724
+ stripe: Stripe$1;
2725
2725
  };
2726
2726
  config: {
2727
2727
  trial?: StripePlan;
@@ -2730,7 +2730,7 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2730
2730
  });
2731
2731
  hasTrial(): boolean;
2732
2732
  getPlanByPriceId(priceId: string): StripePlan | undefined;
2733
- getCustomerByEmail(email: string): ServerResultAsync<Stripe.Customer | null>;
2733
+ getCustomerByEmail(email: string): ServerResultAsync<Stripe$1.Customer | null>;
2734
2734
  getUserByCustomerId(customerId: string): ServerResultAsync<InferSelectModel<Schema["users"]> | null>;
2735
2735
  createCustomer({
2736
2736
  email,
@@ -2740,8 +2740,8 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2740
2740
  email: string;
2741
2741
  name?: string;
2742
2742
  userId: string;
2743
- }): ServerResultAsync<Stripe.Customer>;
2744
- createTrialSubscription(customerId: string): ServerResultAsync<Stripe.Subscription>;
2743
+ }): ServerResultAsync<Stripe$1.Customer>;
2744
+ createTrialSubscription(customerId: string): ServerResultAsync<Stripe$1.Subscription>;
2745
2745
  createSubscription({
2746
2746
  customerId,
2747
2747
  priceId,
@@ -2752,7 +2752,7 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2752
2752
  priceId: string;
2753
2753
  quantity?: number;
2754
2754
  trialDays?: number;
2755
- }): ServerResultAsync<Stripe.Subscription>;
2755
+ }): ServerResultAsync<Stripe$1.Subscription>;
2756
2756
  updateUserCustomerId({
2757
2757
  userId,
2758
2758
  customerId
@@ -2762,7 +2762,7 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2762
2762
  }): ServerResultAsync<InferSelectModel<Schema["users"]>>;
2763
2763
  getLatestSubscription(referenceId: string): ServerResultAsync<BillingSchema | null>;
2764
2764
  getActiveSubscription(referenceId: string): ServerResultAsync<BillingSchema | null>;
2765
- listInvoices(customerId: string): ServerResultAsync<Stripe.Invoice[]>;
2765
+ listInvoices(customerId: string): ServerResultAsync<Stripe$1.Invoice[]>;
2766
2766
  createCheckoutSession({
2767
2767
  customerId,
2768
2768
  priceId,
@@ -2771,8 +2771,8 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2771
2771
  customerId: string;
2772
2772
  priceId: string;
2773
2773
  userId: string;
2774
- }): ServerResultAsync<Stripe.Checkout.Session>;
2775
- createBillingPortalSession(customerId: string): ServerResultAsync<Stripe.BillingPortal.Session>;
2774
+ }): ServerResultAsync<Stripe$1.Checkout.Session>;
2775
+ createBillingPortalSession(customerId: string): ServerResultAsync<Stripe$1.BillingPortal.Session>;
2776
2776
  syncStripeData({
2777
2777
  customerId,
2778
2778
  userId
@@ -2780,7 +2780,7 @@ declare class BillingRepository extends BaseTableRepository<Orm, Schema, Record<
2780
2780
  customerId: string;
2781
2781
  userId: string;
2782
2782
  }): ServerResultAsync<boolean>;
2783
- constructEvent(body: Buffer | string, signature: string, secret: string): ServerResult<Stripe.Event>;
2783
+ constructEvent(body: Buffer | string, signature: string, secret: string): ServerResult<Stripe$1.Event>;
2784
2784
  }
2785
2785
  //#endregion
2786
2786
  export { BillingRepository };
@@ -4,7 +4,7 @@ import { BillingRepository } from "./billing.repository.cjs";
4
4
  import { User } from "../auth/auth.lib.cjs";
5
5
  import { BaseService } from "../base/base.service.cjs";
6
6
  import { BillingSchema } from "@m5kdev/commons/modules/billing/billing.schema";
7
- import Stripe$1 from "stripe";
7
+ import Stripe from "stripe";
8
8
 
9
9
  //#region src/modules/billing/billing.service.d.ts
10
10
  declare class BillingService extends BaseService<{
@@ -18,7 +18,7 @@ declare class BillingService extends BaseService<{
18
18
  email: string;
19
19
  name?: string;
20
20
  };
21
- }): ServerResultAsync<Stripe$1.Customer>;
21
+ }): ServerResultAsync<Stripe.Customer>;
22
22
  createUserHook({
23
23
  user
24
24
  }: {
@@ -29,7 +29,7 @@ declare class BillingService extends BaseService<{
29
29
  };
30
30
  }): ServerResultAsync<boolean>;
31
31
  getActiveSubscription(ctx: Context): ServerResultAsync<BillingSchema | null>;
32
- listInvoices(ctx: Context): ServerResultAsync<Stripe$1.Invoice[]>;
32
+ listInvoices(ctx: Context): ServerResultAsync<Stripe.Invoice[]>;
33
33
  createCheckoutSession({
34
34
  priceId
35
35
  }: {
@@ -38,15 +38,15 @@ declare class BillingService extends BaseService<{
38
38
  user
39
39
  }: {
40
40
  user: User;
41
- }): ServerResultAsync<Stripe$1.Checkout.Session>;
41
+ }): ServerResultAsync<Stripe.Checkout.Session>;
42
42
  createBillingPortalSession({
43
43
  user
44
44
  }: {
45
45
  user: User;
46
- }): ServerResultAsync<Stripe$1.BillingPortal.Session>;
47
- constructEvent(body: Buffer | string, signature: string): ServerResult<Stripe$1.Event>;
46
+ }): ServerResultAsync<Stripe.BillingPortal.Session>;
47
+ constructEvent(body: Buffer | string, signature: string): ServerResult<Stripe.Event>;
48
48
  syncStripeData(customerId: string, eventType?: string): ServerResultAsync<boolean>;
49
- processEvent(event: Stripe$1.Event): ServerResultAsync<boolean>;
49
+ processEvent(event: Stripe.Event): ServerResultAsync<boolean>;
50
50
  }
51
51
  //#endregion
52
52
  export { BillingService };
@@ -10,12 +10,19 @@ let node_child_process = require("node:child_process");
10
10
  let ffmpeg_ffprobe_static = require("ffmpeg-ffprobe-static");
11
11
  ffmpeg_ffprobe_static = require_runtime.__toESM(ffmpeg_ffprobe_static);
12
12
  //#region src/modules/video/video.service.ts
13
- if (!ffmpeg_ffprobe_static.default.ffmpegPath || !ffmpeg_ffprobe_static.default.ffprobePath) throw new Error("FFmpeg or FFprobe not found");
14
13
  const uploadsDir = node_path.default.join(__dirname, "..", "uploads");
15
14
  if (!(0, node_fs.existsSync)(uploadsDir)) (0, node_fs.mkdirSync)(uploadsDir, { recursive: true });
15
+ const resolveFfmpegPath = () => {
16
+ const envPath = process.env.FFMPEG_PATH;
17
+ if (envPath && (0, node_fs.existsSync)(envPath)) return envPath;
18
+ const staticPath = ffmpeg_ffprobe_static.default.ffmpegPath;
19
+ if (typeof staticPath === "string" && (0, node_fs.existsSync)(staticPath)) return staticPath;
20
+ return "ffmpeg";
21
+ };
16
22
  const runFfmpeg = async (args) => {
17
23
  await new Promise((resolve, reject) => {
18
- const child = (0, node_child_process.spawn)(ffmpeg_ffprobe_static.default.ffmpegPath, [...args], { stdio: [
24
+ const ffmpegPath = resolveFfmpegPath();
25
+ const child = (0, node_child_process.spawn)(ffmpegPath, [...args], { stdio: [
19
26
  "ignore",
20
27
  "ignore",
21
28
  "pipe"
@@ -25,7 +32,15 @@ const runFfmpeg = async (args) => {
25
32
  child.stderr?.on("data", (chunk) => {
26
33
  stderr += chunk;
27
34
  });
28
- child.on("error", (error) => reject(error));
35
+ child.on("error", (error) => {
36
+ const details = [
37
+ "Failed to spawn ffmpeg.",
38
+ `Resolved ffmpeg path: ${ffmpegPath}`,
39
+ `FFMPEG_PATH: ${process.env.FFMPEG_PATH ?? "(unset)"}`,
40
+ `ffmpeg-ffprobe-static ffmpegPath: ${ffmpeg_ffprobe_static.default.ffmpegPath ?? "(missing)"}`
41
+ ].join("\n");
42
+ reject(/* @__PURE__ */ new Error(`${details}\n\n${String(error)}`));
43
+ });
29
44
  child.on("close", (code) => {
30
45
  if (code === 0) {
31
46
  resolve();
@@ -1 +1 @@
1
- {"version":3,"file":"video.service.cjs","names":["ffbin","path","BaseService"],"sources":["../../../../src/modules/video/video.service.ts"],"sourcesContent":["import { closeSync, existsSync, mkdirSync, openSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawn } from \"node:child_process\";\r\n//\r\nimport ffbin from \"ffmpeg-ffprobe-static\";\r\nimport { ok } from \"neverthrow\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport type { ServerResultAsync } from \"../base/base.dto\";\r\nimport { BaseService } from \"../base/base.service\";\r\n\r\nif (!ffbin.ffmpegPath || !ffbin.ffprobePath) {\r\n throw new Error(\"FFmpeg or FFprobe not found\");\r\n}\r\n\r\nconst uploadsDir = path.join(__dirname, \"..\", \"uploads\");\r\nif (!existsSync(uploadsDir)) {\r\n mkdirSync(uploadsDir, { recursive: true });\r\n}\r\n\r\nconst runFfmpeg = async (args: readonly string[]): Promise<void> => {\r\n await new Promise<void>((resolve, reject) => {\r\n const child = spawn(ffbin.ffmpegPath as string, [...args], {\r\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\r\n });\r\n\r\n let stderr = \"\";\r\n child.stderr?.setEncoding(\"utf8\");\r\n child.stderr?.on(\"data\", (chunk: string) => {\r\n stderr += chunk;\r\n });\r\n\r\n child.on(\"error\", (error) => reject(error));\r\n child.on(\"close\", (code) => {\r\n if (code === 0) {\r\n resolve();\r\n return;\r\n }\r\n reject(new Error(stderr || `ffmpeg exited with code ${code ?? \"unknown\"}`));\r\n });\r\n });\r\n};\r\n\r\nexport class VideoService extends BaseService<never, never> {\r\n async cut(file: string, start: number, end: number): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const duration = end - start;\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp4`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n file,\r\n \"-ss\",\r\n String(start),\r\n \"-t\",\r\n String(duration),\r\n \"-c:v\",\r\n \"libx264\",\r\n \"-c:a\",\r\n \"copy\",\r\n \"-movflags\",\r\n \"+faststart\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n\r\n async webmToWav(input: string, hz = 48000): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.wav`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-vn\",\r\n \"-c:a\",\r\n \"pcm_s16le\",\r\n \"-ar\",\r\n String(hz),\r\n \"-ac\",\r\n \"2\",\r\n \"-f\",\r\n \"wav\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n return ok(output);\r\n });\r\n }\r\n\r\n async extractAudioMp3(input: string, kbps = 192, streamIndex = 0): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp3`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-map\",\r\n `0:a:${streamIndex}`,\r\n \"-c:a\",\r\n \"libmp3lame\",\r\n \"-b:a\",\r\n `${kbps}k`,\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;AAUA,IAAI,CAACA,sBAAAA,QAAM,cAAc,CAACA,sBAAAA,QAAM,YAC9B,OAAM,IAAI,MAAM,8BAA8B;AAGhD,MAAM,aAAaC,UAAAA,QAAK,KAAK,WAAW,MAAM,UAAU;AACxD,IAAI,EAAA,GAAA,QAAA,YAAY,WAAW,CACzB,EAAA,GAAA,QAAA,WAAU,YAAY,EAAE,WAAW,MAAM,CAAC;AAG5C,MAAM,YAAY,OAAO,SAA2C;AAClE,OAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,SAAA,GAAA,mBAAA,OAAcD,sBAAAA,QAAM,YAAsB,CAAC,GAAG,KAAK,EAAE,EACzD,OAAO;GAAC;GAAU;GAAU;GAAO,EACpC,CAAC;EAEF,IAAI,SAAS;AACb,QAAM,QAAQ,YAAY,OAAO;AACjC,QAAM,QAAQ,GAAG,SAAS,UAAkB;AAC1C,aAAU;IACV;AAEF,QAAM,GAAG,UAAU,UAAU,OAAO,MAAM,CAAC;AAC3C,QAAM,GAAG,UAAU,SAAS;AAC1B,OAAI,SAAS,GAAG;AACd,aAAS;AACT;;AAEF,UAAO,IAAI,MAAM,UAAU,2BAA2B,QAAQ,YAAY,CAAC;IAC3E;GACF;;AAGJ,IAAa,eAAb,cAAkCE,sCAAAA,YAA0B;CAC1D,MAAM,IAAI,MAAc,OAAe,KAAwC;AAC7E,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,WAAW,MAAM;GACvB,MAAM,SAASD,UAAAA,QAAK,KAAK,YAAY,IAAA,GAAA,KAAA,KAAW,CAAC,MAAM;AACvD,OAAI,EAAA,GAAA,QAAA,YAAY,OAAO,CACrB,EAAA,GAAA,QAAA,YAAA,GAAA,QAAA,UAAmB,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO,MAAM;IACb;IACA,OAAO,SAAS;IAChB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,WAAA,GAAA,WAAA,IAAU,OAAO;IACjB;;CAGJ,MAAM,UAAU,OAAe,KAAK,MAAkC;AACpE,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAASA,UAAAA,QAAK,KAAK,YAAY,IAAA,GAAA,KAAA,KAAW,CAAC,MAAM;AACvD,OAAI,EAAA,GAAA,QAAA,YAAY,OAAO,CACrB,EAAA,GAAA,QAAA,YAAA,GAAA,QAAA,UAAmB,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA;IACA;IACA;IACA,OAAO,GAAG;IACV;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AACF,WAAA,GAAA,WAAA,IAAU,OAAO;IACjB;;CAGJ,MAAM,gBAAgB,OAAe,OAAO,KAAK,cAAc,GAA8B;AAC3F,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAASA,UAAAA,QAAK,KAAK,YAAY,IAAA,GAAA,KAAA,KAAW,CAAC,MAAM;AACvD,OAAI,EAAA,GAAA,QAAA,YAAY,OAAO,CACrB,EAAA,GAAA,QAAA,YAAA,GAAA,QAAA,UAAmB,QAAQ,IAAI,CAAC;AAElC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO;IACP;IACA;IACA;IACA,GAAG,KAAK;IACR;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,WAAA,GAAA,WAAA,IAAU,OAAO;IACjB"}
1
+ {"version":3,"file":"video.service.cjs","names":["path","ffbin","BaseService"],"sources":["../../../../src/modules/video/video.service.ts"],"sourcesContent":["import { closeSync, existsSync, mkdirSync, openSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawn } from \"node:child_process\";\r\n//\r\nimport ffbin from \"ffmpeg-ffprobe-static\";\r\nimport { ok } from \"neverthrow\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport type { ServerResultAsync } from \"../base/base.dto\";\r\nimport { BaseService } from \"../base/base.service\";\r\n\r\nconst uploadsDir = path.join(__dirname, \"..\", \"uploads\");\r\nif (!existsSync(uploadsDir)) {\r\n mkdirSync(uploadsDir, { recursive: true });\r\n}\r\n\r\nconst resolveFfmpegPath = (): string => {\r\n const envPath = process.env.FFMPEG_PATH;\r\n if (envPath && existsSync(envPath)) return envPath;\r\n\r\n const staticPath = ffbin.ffmpegPath;\r\n if (typeof staticPath === \"string\" && existsSync(staticPath)) return staticPath;\r\n\r\n return \"ffmpeg\";\r\n};\r\n\r\nconst runFfmpeg = async (args: readonly string[]): Promise<void> => {\r\n await new Promise<void>((resolve, reject) => {\r\n const ffmpegPath = resolveFfmpegPath();\r\n const child = spawn(ffmpegPath, [...args], {\r\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\r\n });\r\n\r\n let stderr = \"\";\r\n child.stderr?.setEncoding(\"utf8\");\r\n child.stderr?.on(\"data\", (chunk: string) => {\r\n stderr += chunk;\r\n });\r\n\r\n child.on(\"error\", (error) => {\r\n const details = [\r\n \"Failed to spawn ffmpeg.\",\r\n `Resolved ffmpeg path: ${ffmpegPath}`,\r\n `FFMPEG_PATH: ${process.env.FFMPEG_PATH ?? \"(unset)\"}`,\r\n `ffmpeg-ffprobe-static ffmpegPath: ${ffbin.ffmpegPath ?? \"(missing)\"}`,\r\n ].join(\"\\n\");\r\n reject(new Error(`${details}\\n\\n${String(error)}`));\r\n });\r\n child.on(\"close\", (code) => {\r\n if (code === 0) {\r\n resolve();\r\n return;\r\n }\r\n reject(new Error(stderr || `ffmpeg exited with code ${code ?? \"unknown\"}`));\r\n });\r\n });\r\n};\r\n\r\nexport class VideoService extends BaseService<never, never> {\r\n async cut(file: string, start: number, end: number): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const duration = end - start;\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp4`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n file,\r\n \"-ss\",\r\n String(start),\r\n \"-t\",\r\n String(duration),\r\n \"-c:v\",\r\n \"libx264\",\r\n \"-c:a\",\r\n \"copy\",\r\n \"-movflags\",\r\n \"+faststart\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n\r\n async webmToWav(input: string, hz = 48000): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.wav`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-vn\",\r\n \"-c:a\",\r\n \"pcm_s16le\",\r\n \"-ar\",\r\n String(hz),\r\n \"-ac\",\r\n \"2\",\r\n \"-f\",\r\n \"wav\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n return ok(output);\r\n });\r\n }\r\n\r\n async extractAudioMp3(input: string, kbps = 192, streamIndex = 0): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp3`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-map\",\r\n `0:a:${streamIndex}`,\r\n \"-c:a\",\r\n \"libmp3lame\",\r\n \"-b:a\",\r\n `${kbps}k`,\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -6,12 +6,19 @@ import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
6
6
  import { spawn } from "node:child_process";
7
7
  import ffbin from "ffmpeg-ffprobe-static";
8
8
  //#region src/modules/video/video.service.ts
9
- if (!ffbin.ffmpegPath || !ffbin.ffprobePath) throw new Error("FFmpeg or FFprobe not found");
10
9
  const uploadsDir = path.join(__dirname, "..", "uploads");
11
10
  if (!existsSync(uploadsDir)) mkdirSync(uploadsDir, { recursive: true });
11
+ const resolveFfmpegPath = () => {
12
+ const envPath = process.env.FFMPEG_PATH;
13
+ if (envPath && existsSync(envPath)) return envPath;
14
+ const staticPath = ffbin.ffmpegPath;
15
+ if (typeof staticPath === "string" && existsSync(staticPath)) return staticPath;
16
+ return "ffmpeg";
17
+ };
12
18
  const runFfmpeg = async (args) => {
13
19
  await new Promise((resolve, reject) => {
14
- const child = spawn(ffbin.ffmpegPath, [...args], { stdio: [
20
+ const ffmpegPath = resolveFfmpegPath();
21
+ const child = spawn(ffmpegPath, [...args], { stdio: [
15
22
  "ignore",
16
23
  "ignore",
17
24
  "pipe"
@@ -21,7 +28,15 @@ const runFfmpeg = async (args) => {
21
28
  child.stderr?.on("data", (chunk) => {
22
29
  stderr += chunk;
23
30
  });
24
- child.on("error", (error) => reject(error));
31
+ child.on("error", (error) => {
32
+ const details = [
33
+ "Failed to spawn ffmpeg.",
34
+ `Resolved ffmpeg path: ${ffmpegPath}`,
35
+ `FFMPEG_PATH: ${process.env.FFMPEG_PATH ?? "(unset)"}`,
36
+ `ffmpeg-ffprobe-static ffmpegPath: ${ffbin.ffmpegPath ?? "(missing)"}`
37
+ ].join("\n");
38
+ reject(/* @__PURE__ */ new Error(`${details}\n\n${String(error)}`));
39
+ });
25
40
  child.on("close", (code) => {
26
41
  if (code === 0) {
27
42
  resolve();
@@ -1 +1 @@
1
- {"version":3,"file":"video.service.mjs","names":["uuidv4"],"sources":["../../../../src/modules/video/video.service.ts"],"sourcesContent":["import { closeSync, existsSync, mkdirSync, openSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawn } from \"node:child_process\";\r\n//\r\nimport ffbin from \"ffmpeg-ffprobe-static\";\r\nimport { ok } from \"neverthrow\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport type { ServerResultAsync } from \"../base/base.dto\";\r\nimport { BaseService } from \"../base/base.service\";\r\n\r\nif (!ffbin.ffmpegPath || !ffbin.ffprobePath) {\r\n throw new Error(\"FFmpeg or FFprobe not found\");\r\n}\r\n\r\nconst uploadsDir = path.join(__dirname, \"..\", \"uploads\");\r\nif (!existsSync(uploadsDir)) {\r\n mkdirSync(uploadsDir, { recursive: true });\r\n}\r\n\r\nconst runFfmpeg = async (args: readonly string[]): Promise<void> => {\r\n await new Promise<void>((resolve, reject) => {\r\n const child = spawn(ffbin.ffmpegPath as string, [...args], {\r\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\r\n });\r\n\r\n let stderr = \"\";\r\n child.stderr?.setEncoding(\"utf8\");\r\n child.stderr?.on(\"data\", (chunk: string) => {\r\n stderr += chunk;\r\n });\r\n\r\n child.on(\"error\", (error) => reject(error));\r\n child.on(\"close\", (code) => {\r\n if (code === 0) {\r\n resolve();\r\n return;\r\n }\r\n reject(new Error(stderr || `ffmpeg exited with code ${code ?? \"unknown\"}`));\r\n });\r\n });\r\n};\r\n\r\nexport class VideoService extends BaseService<never, never> {\r\n async cut(file: string, start: number, end: number): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const duration = end - start;\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp4`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n file,\r\n \"-ss\",\r\n String(start),\r\n \"-t\",\r\n String(duration),\r\n \"-c:v\",\r\n \"libx264\",\r\n \"-c:a\",\r\n \"copy\",\r\n \"-movflags\",\r\n \"+faststart\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n\r\n async webmToWav(input: string, hz = 48000): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.wav`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-vn\",\r\n \"-c:a\",\r\n \"pcm_s16le\",\r\n \"-ar\",\r\n String(hz),\r\n \"-ac\",\r\n \"2\",\r\n \"-f\",\r\n \"wav\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n return ok(output);\r\n });\r\n }\r\n\r\n async extractAudioMp3(input: string, kbps = 192, streamIndex = 0): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp3`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-map\",\r\n `0:a:${streamIndex}`,\r\n \"-c:a\",\r\n \"libmp3lame\",\r\n \"-b:a\",\r\n `${kbps}k`,\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n}\r\n"],"mappings":";;;;;;;;AAUA,IAAI,CAAC,MAAM,cAAc,CAAC,MAAM,YAC9B,OAAM,IAAI,MAAM,8BAA8B;AAGhD,MAAM,aAAa,KAAK,KAAK,WAAW,MAAM,UAAU;AACxD,IAAI,CAAC,WAAW,WAAW,CACzB,WAAU,YAAY,EAAE,WAAW,MAAM,CAAC;AAG5C,MAAM,YAAY,OAAO,SAA2C;AAClE,OAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,QAAQ,MAAM,MAAM,YAAsB,CAAC,GAAG,KAAK,EAAE,EACzD,OAAO;GAAC;GAAU;GAAU;GAAO,EACpC,CAAC;EAEF,IAAI,SAAS;AACb,QAAM,QAAQ,YAAY,OAAO;AACjC,QAAM,QAAQ,GAAG,SAAS,UAAkB;AAC1C,aAAU;IACV;AAEF,QAAM,GAAG,UAAU,UAAU,OAAO,MAAM,CAAC;AAC3C,QAAM,GAAG,UAAU,SAAS;AAC1B,OAAI,SAAS,GAAG;AACd,aAAS;AACT;;AAEF,UAAO,IAAI,MAAM,UAAU,2BAA2B,QAAQ,YAAY,CAAC;IAC3E;GACF;;AAGJ,IAAa,eAAb,cAAkC,YAA0B;CAC1D,MAAM,IAAI,MAAc,OAAe,KAAwC;AAC7E,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,WAAW,MAAM;GACvB,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO,MAAM;IACb;IACA,OAAO,SAAS;IAChB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,UAAO,GAAG,OAAO;IACjB;;CAGJ,MAAM,UAAU,OAAe,KAAK,MAAkC;AACpE,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA;IACA;IACA;IACA,OAAO,GAAG;IACV;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AACF,UAAO,GAAG,OAAO;IACjB;;CAGJ,MAAM,gBAAgB,OAAe,OAAO,KAAK,cAAc,GAA8B;AAC3F,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAElC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO;IACP;IACA;IACA;IACA,GAAG,KAAK;IACR;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,UAAO,GAAG,OAAO;IACjB"}
1
+ {"version":3,"file":"video.service.mjs","names":["uuidv4"],"sources":["../../../../src/modules/video/video.service.ts"],"sourcesContent":["import { closeSync, existsSync, mkdirSync, openSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawn } from \"node:child_process\";\r\n//\r\nimport ffbin from \"ffmpeg-ffprobe-static\";\r\nimport { ok } from \"neverthrow\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport type { ServerResultAsync } from \"../base/base.dto\";\r\nimport { BaseService } from \"../base/base.service\";\r\n\r\nconst uploadsDir = path.join(__dirname, \"..\", \"uploads\");\r\nif (!existsSync(uploadsDir)) {\r\n mkdirSync(uploadsDir, { recursive: true });\r\n}\r\n\r\nconst resolveFfmpegPath = (): string => {\r\n const envPath = process.env.FFMPEG_PATH;\r\n if (envPath && existsSync(envPath)) return envPath;\r\n\r\n const staticPath = ffbin.ffmpegPath;\r\n if (typeof staticPath === \"string\" && existsSync(staticPath)) return staticPath;\r\n\r\n return \"ffmpeg\";\r\n};\r\n\r\nconst runFfmpeg = async (args: readonly string[]): Promise<void> => {\r\n await new Promise<void>((resolve, reject) => {\r\n const ffmpegPath = resolveFfmpegPath();\r\n const child = spawn(ffmpegPath, [...args], {\r\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\r\n });\r\n\r\n let stderr = \"\";\r\n child.stderr?.setEncoding(\"utf8\");\r\n child.stderr?.on(\"data\", (chunk: string) => {\r\n stderr += chunk;\r\n });\r\n\r\n child.on(\"error\", (error) => {\r\n const details = [\r\n \"Failed to spawn ffmpeg.\",\r\n `Resolved ffmpeg path: ${ffmpegPath}`,\r\n `FFMPEG_PATH: ${process.env.FFMPEG_PATH ?? \"(unset)\"}`,\r\n `ffmpeg-ffprobe-static ffmpegPath: ${ffbin.ffmpegPath ?? \"(missing)\"}`,\r\n ].join(\"\\n\");\r\n reject(new Error(`${details}\\n\\n${String(error)}`));\r\n });\r\n child.on(\"close\", (code) => {\r\n if (code === 0) {\r\n resolve();\r\n return;\r\n }\r\n reject(new Error(stderr || `ffmpeg exited with code ${code ?? \"unknown\"}`));\r\n });\r\n });\r\n};\r\n\r\nexport class VideoService extends BaseService<never, never> {\r\n async cut(file: string, start: number, end: number): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const duration = end - start;\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp4`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n file,\r\n \"-ss\",\r\n String(start),\r\n \"-t\",\r\n String(duration),\r\n \"-c:v\",\r\n \"libx264\",\r\n \"-c:a\",\r\n \"copy\",\r\n \"-movflags\",\r\n \"+faststart\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n\r\n async webmToWav(input: string, hz = 48000): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.wav`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-vn\",\r\n \"-c:a\",\r\n \"pcm_s16le\",\r\n \"-ar\",\r\n String(hz),\r\n \"-ac\",\r\n \"2\",\r\n \"-f\",\r\n \"wav\",\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n return ok(output);\r\n });\r\n }\r\n\r\n async extractAudioMp3(input: string, kbps = 192, streamIndex = 0): ServerResultAsync<string> {\r\n return this.throwableAsync(async () => {\r\n const output = path.join(uploadsDir, `${uuidv4()}.mp3`);\r\n if (!existsSync(output)) {\r\n closeSync(openSync(output, \"w\"));\r\n }\r\n await runFfmpeg([\r\n \"-i\",\r\n input,\r\n \"-map\",\r\n `0:a:${streamIndex}`,\r\n \"-c:a\",\r\n \"libmp3lame\",\r\n \"-b:a\",\r\n `${kbps}k`,\r\n \"-y\",\r\n output,\r\n ]).catch((error) => {\r\n throw this.handleUnknownError(error);\r\n });\r\n\r\n return ok(output);\r\n });\r\n }\r\n}\r\n"],"mappings":";;;;;;;;AAUA,MAAM,aAAa,KAAK,KAAK,WAAW,MAAM,UAAU;AACxD,IAAI,CAAC,WAAW,WAAW,CACzB,WAAU,YAAY,EAAE,WAAW,MAAM,CAAC;AAG5C,MAAM,0BAAkC;CACtC,MAAM,UAAU,QAAQ,IAAI;AAC5B,KAAI,WAAW,WAAW,QAAQ,CAAE,QAAO;CAE3C,MAAM,aAAa,MAAM;AACzB,KAAI,OAAO,eAAe,YAAY,WAAW,WAAW,CAAE,QAAO;AAErE,QAAO;;AAGT,MAAM,YAAY,OAAO,SAA2C;AAClE,OAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,aAAa,mBAAmB;EACtC,MAAM,QAAQ,MAAM,YAAY,CAAC,GAAG,KAAK,EAAE,EACzC,OAAO;GAAC;GAAU;GAAU;GAAO,EACpC,CAAC;EAEF,IAAI,SAAS;AACb,QAAM,QAAQ,YAAY,OAAO;AACjC,QAAM,QAAQ,GAAG,SAAS,UAAkB;AAC1C,aAAU;IACV;AAEF,QAAM,GAAG,UAAU,UAAU;GAC3B,MAAM,UAAU;IACd;IACA,yBAAyB;IACzB,gBAAgB,QAAQ,IAAI,eAAe;IAC3C,qCAAqC,MAAM,cAAc;IAC1D,CAAC,KAAK,KAAK;AACZ,0BAAO,IAAI,MAAM,GAAG,QAAQ,MAAM,OAAO,MAAM,GAAG,CAAC;IACnD;AACF,QAAM,GAAG,UAAU,SAAS;AAC1B,OAAI,SAAS,GAAG;AACd,aAAS;AACT;;AAEF,UAAO,IAAI,MAAM,UAAU,2BAA2B,QAAQ,YAAY,CAAC;IAC3E;GACF;;AAGJ,IAAa,eAAb,cAAkC,YAA0B;CAC1D,MAAM,IAAI,MAAc,OAAe,KAAwC;AAC7E,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,WAAW,MAAM;GACvB,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO,MAAM;IACb;IACA,OAAO,SAAS;IAChB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,UAAO,GAAG,OAAO;IACjB;;CAGJ,MAAM,UAAU,OAAe,KAAK,MAAkC;AACpE,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAGlC,SAAM,UAAU;IACd;IACA;IACA;IACA;IACA;IACA;IACA,OAAO,GAAG;IACV;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AACF,UAAO,GAAG,OAAO;IACjB;;CAGJ,MAAM,gBAAgB,OAAe,OAAO,KAAK,cAAc,GAA8B;AAC3F,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,SAAS,KAAK,KAAK,YAAY,GAAGA,IAAQ,CAAC,MAAM;AACvD,OAAI,CAAC,WAAW,OAAO,CACrB,WAAU,SAAS,QAAQ,IAAI,CAAC;AAElC,SAAM,UAAU;IACd;IACA;IACA;IACA,OAAO;IACP;IACA;IACA;IACA,GAAG,KAAK;IACR;IACA;IACD,CAAC,CAAC,OAAO,UAAU;AAClB,UAAM,KAAK,mBAAmB,MAAM;KACpC;AAEF,UAAO,GAAG,OAAO;IACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m5kdev/backend",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Composable Express server stack with Drizzle ORM and tRPC.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
@@ -61,8 +61,8 @@
61
61
  "turndown": "7.2.2",
62
62
  "uuid": "11.0.5",
63
63
  "zod": "4.2.1",
64
- "@m5kdev/config": "0.9.3",
65
- "@m5kdev/commons": "0.9.3"
64
+ "@m5kdev/commons": "0.9.4",
65
+ "@m5kdev/config": "0.9.4"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@jest/globals": "30.2.0",