@m5kdev/backend 0.8.6 → 0.8.7

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.
@@ -12,8 +12,8 @@ declare const waitlistSchema: z.ZodObject<{
12
12
  expiresAt: z.ZodNullable<z.ZodDate>;
13
13
  }, z.core.$strip>;
14
14
  declare const waitlistOutputSchema: z.ZodObject<{
15
- name: z.ZodNullable<z.ZodString>;
16
15
  id: z.ZodString;
16
+ name: z.ZodNullable<z.ZodString>;
17
17
  email: z.ZodNullable<z.ZodString>;
18
18
  createdAt: z.ZodDate;
19
19
  updatedAt: z.ZodNullable<z.ZodDate>;
@@ -36,8 +36,8 @@ declare const accountClaimOutputSchema: z.ZodObject<{
36
36
  id: z.ZodString;
37
37
  createdAt: z.ZodDate;
38
38
  updatedAt: z.ZodNullable<z.ZodDate>;
39
- status: z.ZodString;
40
39
  expiresAt: z.ZodNullable<z.ZodDate>;
40
+ status: z.ZodString;
41
41
  claimUserId: z.ZodNullable<z.ZodString>;
42
42
  claimedAt: z.ZodNullable<z.ZodDate>;
43
43
  claimedEmail: z.ZodNullable<z.ZodString>;
@@ -4830,7 +4830,7 @@ declare function createBetterAuth<O extends Orm, S extends Schema, E extends Ema
4830
4830
  } | undefined;
4831
4831
  verification?: {
4832
4832
  modelName?: string;
4833
- fields?: Partial<Record<"createdAt" | "updatedAt" | "expiresAt" | "value" | "identifier", string>>;
4833
+ fields?: Partial<Record<"createdAt" | "updatedAt" | "expiresAt" | "identifier" | "value", string>>;
4834
4834
  additionalFields?: {
4835
4835
  [key: string]: _$better_auth0.DBFieldAttribute;
4836
4836
  };
@@ -59,8 +59,8 @@ declare function createAuthTRPC({
59
59
  id: string;
60
60
  createdAt: Date;
61
61
  updatedAt: Date | null;
62
- status: string;
63
62
  expiresAt: Date | null;
63
+ status: string;
64
64
  claimUserId: string | null;
65
65
  claimedAt: Date | null;
66
66
  claimedEmail: string | null;
@@ -146,8 +146,8 @@ declare function createAuthTRPC({
146
146
  listAdminWaitlist: _$_trpc_server0.TRPCQueryProcedure<{
147
147
  input: void;
148
148
  output: {
149
- name: string | null;
150
149
  id: string;
150
+ name: string | null;
151
151
  email: string | null;
152
152
  createdAt: Date;
153
153
  updatedAt: Date | null;
@@ -160,8 +160,8 @@ declare function createAuthTRPC({
160
160
  email: string;
161
161
  };
162
162
  output: {
163
- name: string | null;
164
163
  id: string;
164
+ name: string | null;
165
165
  email: string | null;
166
166
  createdAt: Date;
167
167
  updatedAt: Date | null;
@@ -191,8 +191,8 @@ declare function createAuthTRPC({
191
191
  id: string;
192
192
  };
193
193
  output: {
194
- name: string | null;
195
194
  id: string;
195
+ name: string | null;
196
196
  email: string | null;
197
197
  createdAt: Date;
198
198
  updatedAt: Date | null;
@@ -205,8 +205,8 @@ declare function createAuthTRPC({
205
205
  id: string;
206
206
  };
207
207
  output: {
208
- name: string | null;
209
208
  id: string;
209
+ name: string | null;
210
210
  email: string | null;
211
211
  createdAt: Date;
212
212
  updatedAt: Date | null;
@@ -219,8 +219,8 @@ declare function createAuthTRPC({
219
219
  email: string;
220
220
  };
221
221
  output: {
222
- name: string | null;
223
222
  id: string;
223
+ name: string | null;
224
224
  email: string | null;
225
225
  createdAt: Date;
226
226
  updatedAt: Date | null;
@@ -4,7 +4,7 @@ import { BillingSchema } from "@m5kdev/commons/modules/billing/billing.schema";
4
4
  import { InferSelectModel } from "drizzle-orm";
5
5
  import * as _$drizzle_orm_sqlite_core0 from "drizzle-orm/sqlite-core";
6
6
  import { LibSQLDatabase } from "drizzle-orm/libsql";
7
- import { Stripe as Stripe$1 } from "stripe";
7
+ import { Stripe } 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$1;
2716
+ stripe: Stripe;
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$1;
2724
+ stripe: Stripe;
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$1.Customer | null>;
2733
+ getCustomerByEmail(email: string): ServerResultAsync<Stripe.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$1.Customer>;
2744
- createTrialSubscription(customerId: string): ServerResultAsync<Stripe$1.Subscription>;
2743
+ }): ServerResultAsync<Stripe.Customer>;
2744
+ createTrialSubscription(customerId: string): ServerResultAsync<Stripe.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$1.Subscription>;
2755
+ }): ServerResultAsync<Stripe.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$1.Invoice[]>;
2765
+ listInvoices(customerId: string): ServerResultAsync<Stripe.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$1.Checkout.Session>;
2775
- createBillingPortalSession(customerId: string): ServerResultAsync<Stripe$1.BillingPortal.Session>;
2774
+ }): ServerResultAsync<Stripe.Checkout.Session>;
2775
+ createBillingPortalSession(customerId: string): ServerResultAsync<Stripe.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$1.Event>;
2783
+ constructEvent(body: Buffer | string, signature: string, secret: string): ServerResult<Stripe.Event>;
2784
2784
  }
2785
2785
  //#endregion
2786
2786
  export { BillingRepository };
@@ -4,7 +4,7 @@ import { BillingRepository } from "./billing.repository.mjs";
4
4
  import { User } from "../auth/auth.lib.mjs";
5
5
  import { BaseService } from "../base/base.service.mjs";
6
6
  import { BillingSchema } from "@m5kdev/commons/modules/billing/billing.schema";
7
- import Stripe from "stripe";
7
+ import Stripe$1 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.Customer>;
21
+ }): ServerResultAsync<Stripe$1.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.Invoice[]>;
32
+ listInvoices(ctx: Context): ServerResultAsync<Stripe$1.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.Checkout.Session>;
41
+ }): ServerResultAsync<Stripe$1.Checkout.Session>;
42
42
  createBillingPortalSession({
43
43
  user
44
44
  }: {
45
45
  user: User;
46
- }): ServerResultAsync<Stripe.BillingPortal.Session>;
47
- constructEvent(body: Buffer | string, signature: string): ServerResult<Stripe.Event>;
46
+ }): ServerResultAsync<Stripe$1.BillingPortal.Session>;
47
+ constructEvent(body: Buffer | string, signature: string): ServerResult<Stripe$1.Event>;
48
48
  syncStripeData(customerId: string, eventType?: string): ServerResultAsync<boolean>;
49
- processEvent(event: Stripe.Event): ServerResultAsync<boolean>;
49
+ processEvent(event: Stripe$1.Event): ServerResultAsync<boolean>;
50
50
  }
51
51
  //#endregion
52
52
  export { BillingService };
@@ -29,8 +29,8 @@ declare const connectSelectOutputSchema: z.ZodObject<{
29
29
  updatedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
30
30
  expiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
31
31
  userId: z.ZodString;
32
- provider: z.ZodString;
33
32
  scope: z.ZodOptional<z.ZodNullable<z.ZodString>>;
33
+ provider: z.ZodString;
34
34
  accountType: z.ZodString;
35
35
  providerAccountId: z.ZodString;
36
36
  handle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -53,8 +53,8 @@ declare const connectListOutputSchema: z.ZodArray<z.ZodObject<{
53
53
  updatedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
54
54
  expiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
55
55
  userId: z.ZodString;
56
- provider: z.ZodString;
57
56
  scope: z.ZodOptional<z.ZodNullable<z.ZodString>>;
57
+ provider: z.ZodString;
58
58
  accountType: z.ZodString;
59
59
  providerAccountId: z.ZodString;
60
60
  handle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -398,10 +398,10 @@ declare class ConnectRepository extends BaseTableRepository<Orm, Schema, Record<
398
398
  updatedAt: Date | null;
399
399
  expiresAt: Date | null;
400
400
  userId: string;
401
- provider: string;
402
401
  accessToken: string;
403
402
  refreshToken: string | null;
404
403
  scope: string | null;
404
+ provider: string;
405
405
  accountType: string;
406
406
  providerAccountId: string;
407
407
  handle: string | null;
@@ -24,10 +24,10 @@ declare class ConnectService extends BaseService<{
24
24
  updatedAt: Date | null;
25
25
  expiresAt: Date | null;
26
26
  userId: string;
27
- provider: string;
28
27
  accessToken: string;
29
28
  refreshToken: string | null;
30
29
  scope: string | null;
30
+ provider: string;
31
31
  accountType: string;
32
32
  providerAccountId: string;
33
33
  handle: string | null;
@@ -45,10 +45,10 @@ declare class ConnectService extends BaseService<{
45
45
  updatedAt: Date | null;
46
46
  expiresAt: Date | null;
47
47
  userId: string;
48
- provider: string;
49
48
  accessToken: string;
50
49
  refreshToken: string | null;
51
50
  scope: string | null;
51
+ provider: string;
52
52
  accountType: string;
53
53
  providerAccountId: string;
54
54
  handle: string | null;
@@ -18,7 +18,7 @@ declare class RecurrenceService extends BaseService<{
18
18
  filters?: {
19
19
  columnId: string;
20
20
  type: "string" | "number" | "boolean" | "date" | "enum";
21
- method: "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "on" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
21
+ method: "contains" | "starts_with" | "ends_with" | "intersect" | "on" | "equals" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
22
22
  value: string | number | boolean | string[];
23
23
  valueTo?: string | undefined;
24
24
  endColumnId?: string | undefined;
@@ -29,12 +29,12 @@ declare class RecurrenceService extends BaseService<{
29
29
  actor: UserActor;
30
30
  }, {
31
31
  rows: {
32
- name: string | null;
33
32
  id: string;
33
+ name: string | null;
34
34
  createdAt: Date;
35
35
  updatedAt: Date;
36
- userId: string | null;
37
36
  metadata: Record<string, any> | null;
37
+ userId: string | null;
38
38
  organizationId: string | null;
39
39
  teamId: string | null;
40
40
  enabled: boolean;
@@ -21,7 +21,7 @@ declare function createRecurrenceTRPC({
21
21
  filters?: {
22
22
  columnId: string;
23
23
  type: "string" | "number" | "boolean" | "date" | "enum";
24
- method: "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "on" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
24
+ method: "contains" | "starts_with" | "ends_with" | "intersect" | "on" | "equals" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
25
25
  value: string | number | boolean | string[];
26
26
  valueTo?: string | undefined;
27
27
  endColumnId?: string | undefined;
@@ -21,7 +21,7 @@ declare function createTagTRPC({
21
21
  filters?: {
22
22
  columnId: string;
23
23
  type: "string" | "number" | "boolean" | "date" | "enum";
24
- method: "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "on" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
24
+ method: "contains" | "starts_with" | "ends_with" | "intersect" | "on" | "equals" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
25
25
  value: string | number | boolean | string[];
26
26
  valueTo?: string | undefined;
27
27
  endColumnId?: string | undefined;
@@ -54,7 +54,7 @@ declare function createTagTRPC({
54
54
  resourceIds?: {
55
55
  columnId: string;
56
56
  type: "string" | "number" | "boolean" | "date" | "enum";
57
- method: "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "on" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
57
+ method: "contains" | "starts_with" | "ends_with" | "intersect" | "on" | "equals" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
58
58
  value: string | number | boolean | string[];
59
59
  valueTo?: string | undefined;
60
60
  endColumnId?: string | undefined;
@@ -1,25 +1,60 @@
1
1
  import { BaseService } from "../base/base.service.mjs";
2
- import { err, ok } from "neverthrow";
2
+ import { ok } from "neverthrow";
3
3
  import { v4 } from "uuid";
4
4
  import path from "node:path";
5
5
  import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
6
+ import { spawn } from "node:child_process";
6
7
  import ffbin from "ffmpeg-ffprobe-static";
7
- import ffmpeg from "fluent-ffmpeg";
8
8
  //#region src/modules/video/video.service.ts
9
9
  if (!ffbin.ffmpegPath || !ffbin.ffprobePath) throw new Error("FFmpeg or FFprobe not found");
10
- ffmpeg.setFfmpegPath(ffbin.ffmpegPath);
11
- ffmpeg.setFfprobePath(ffbin.ffprobePath);
12
10
  const uploadsDir = path.join(__dirname, "..", "uploads");
13
11
  if (!existsSync(uploadsDir)) mkdirSync(uploadsDir, { recursive: true });
12
+ const runFfmpeg = async (args) => {
13
+ await new Promise((resolve, reject) => {
14
+ const child = spawn(ffbin.ffmpegPath, [...args], { stdio: [
15
+ "ignore",
16
+ "ignore",
17
+ "pipe"
18
+ ] });
19
+ let stderr = "";
20
+ child.stderr?.setEncoding("utf8");
21
+ child.stderr?.on("data", (chunk) => {
22
+ stderr += chunk;
23
+ });
24
+ child.on("error", (error) => reject(error));
25
+ child.on("close", (code) => {
26
+ if (code === 0) {
27
+ resolve();
28
+ return;
29
+ }
30
+ reject(new Error(stderr || `ffmpeg exited with code ${code ?? "unknown"}`));
31
+ });
32
+ });
33
+ };
14
34
  var VideoService = class extends BaseService {
15
35
  async cut(file, start, end) {
16
36
  return this.throwableAsync(async () => {
17
37
  const duration = end - start;
18
38
  const output = path.join(uploadsDir, `${v4()}.mp4`);
19
39
  if (!existsSync(output)) closeSync(openSync(output, "w"));
20
- await new Promise((resolve, reject) => {
21
- ffmpeg(file).seekOutput(start).videoCodec("libx264").audioCodec("copy").outputOptions(["-y", "-movflags +faststart"]).duration(duration).on("end", () => resolve()).on("error", (e, _stdout, _stderr) => reject(e)).save(output);
22
- }).catch((error) => err(this.handleUnknownError(error)));
40
+ await runFfmpeg([
41
+ "-i",
42
+ file,
43
+ "-ss",
44
+ String(start),
45
+ "-t",
46
+ String(duration),
47
+ "-c:v",
48
+ "libx264",
49
+ "-c:a",
50
+ "copy",
51
+ "-movflags",
52
+ "+faststart",
53
+ "-y",
54
+ output
55
+ ]).catch((error) => {
56
+ throw this.handleUnknownError(error);
57
+ });
23
58
  return ok(output);
24
59
  });
25
60
  }
@@ -27,9 +62,23 @@ var VideoService = class extends BaseService {
27
62
  return this.throwableAsync(async () => {
28
63
  const output = path.join(uploadsDir, `${v4()}.wav`);
29
64
  if (!existsSync(output)) closeSync(openSync(output, "w"));
30
- await new Promise((resolve, reject) => {
31
- ffmpeg(input).noVideo().audioCodec("pcm_s16le").audioFrequency(hz).audioChannels(2).format("wav").outputOptions(["-y"]).on("end", () => resolve()).on("error", reject).save(output);
32
- }).catch((error) => err(this.handleUnknownError(error)));
65
+ await runFfmpeg([
66
+ "-i",
67
+ input,
68
+ "-vn",
69
+ "-c:a",
70
+ "pcm_s16le",
71
+ "-ar",
72
+ String(hz),
73
+ "-ac",
74
+ "2",
75
+ "-f",
76
+ "wav",
77
+ "-y",
78
+ output
79
+ ]).catch((error) => {
80
+ throw this.handleUnknownError(error);
81
+ });
33
82
  return ok(output);
34
83
  });
35
84
  }
@@ -37,9 +86,20 @@ var VideoService = class extends BaseService {
37
86
  return this.throwableAsync(async () => {
38
87
  const output = path.join(uploadsDir, `${v4()}.mp3`);
39
88
  if (!existsSync(output)) closeSync(openSync(output, "w"));
40
- await new Promise((resolve, reject) => {
41
- ffmpeg(input).outputOptions(["-y", `-map 0:a:${streamIndex}`]).audioCodec("libmp3lame").audioBitrate(kbps).on("end", () => resolve()).on("error", reject).save(output);
42
- }).catch((error) => err(this.handleUnknownError(error)));
89
+ await runFfmpeg([
90
+ "-i",
91
+ input,
92
+ "-map",
93
+ `0:a:${streamIndex}`,
94
+ "-c:a",
95
+ "libmp3lame",
96
+ "-b:a",
97
+ `${kbps}k`,
98
+ "-y",
99
+ output
100
+ ]).catch((error) => {
101
+ throw this.handleUnknownError(error);
102
+ });
43
103
  return ok(output);
44
104
  });
45
105
  }
@@ -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\n//\r\nimport ffbin from \"ffmpeg-ffprobe-static\";\r\nimport ffmpeg from \"fluent-ffmpeg\";\r\nimport { err, 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\nffmpeg.setFfmpegPath(ffbin.ffmpegPath);\r\nffmpeg.setFfprobePath(ffbin.ffprobePath);\r\n\r\nconst uploadsDir = path.join(__dirname, \"..\", \"uploads\");\r\nif (!existsSync(uploadsDir)) {\r\n mkdirSync(uploadsDir, { recursive: true });\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 new Promise<void>((resolve, reject) => {\r\n ffmpeg(file)\r\n .seekOutput(start)\r\n .videoCodec(\"libx264\")\r\n .audioCodec(\"copy\")\r\n .outputOptions([\"-y\", \"-movflags +faststart\"])\r\n .duration(duration)\r\n .on(\"end\", () => resolve())\r\n .on(\"error\", (e: Error, _stdout: string | null, _stderr: string | null) => reject(e))\r\n .save(output);\r\n }).catch((error) => err(this.handleUnknownError(error)));\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 await new Promise<void>((resolve, reject) => {\r\n ffmpeg(input)\r\n .noVideo()\r\n .audioCodec(\"pcm_s16le\") // WAV PCM 16-bit\r\n .audioFrequency(hz) // 48000 or 44100\r\n .audioChannels(2) // down/up-mix as needed\r\n .format(\"wav\")\r\n .outputOptions([\"-y\"])\r\n .on(\"end\", () => resolve())\r\n .on(\"error\", reject)\r\n .save(output);\r\n }).catch((error) => err(this.handleUnknownError(error)));\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 new Promise<void>((resolve, reject) => {\r\n ffmpeg(input)\r\n .outputOptions([\"-y\", `-map 0:a:${streamIndex}`])\r\n .audioCodec(\"libmp3lame\")\r\n .audioBitrate(kbps)\r\n .on(\"end\", () => resolve())\r\n .on(\"error\", reject)\r\n .save(output);\r\n }).catch((error) => err(this.handleUnknownError(error)));\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,OAAO,cAAc,MAAM,WAAW;AACtC,OAAO,eAAe,MAAM,YAAY;AAExC,MAAM,aAAa,KAAK,KAAK,WAAW,MAAM,UAAU;AACxD,IAAI,CAAC,WAAW,WAAW,CACzB,WAAU,YAAY,EAAE,WAAW,MAAM,CAAC;AAG5C,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,IAAI,SAAe,SAAS,WAAW;AAC3C,WAAO,KAAK,CACT,WAAW,MAAM,CACjB,WAAW,UAAU,CACrB,WAAW,OAAO,CAClB,cAAc,CAAC,MAAM,uBAAuB,CAAC,CAC7C,SAAS,SAAS,CAClB,GAAG,aAAa,SAAS,CAAC,CAC1B,GAAG,UAAU,GAAU,SAAwB,YAA2B,OAAO,EAAE,CAAC,CACpF,KAAK,OAAO;KACf,CAAC,OAAO,UAAU,IAAI,KAAK,mBAAmB,MAAM,CAAC,CAAC;AAExD,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;AAElC,SAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,WAAO,MAAM,CACV,SAAS,CACT,WAAW,YAAY,CACvB,eAAe,GAAG,CAClB,cAAc,EAAE,CAChB,OAAO,MAAM,CACb,cAAc,CAAC,KAAK,CAAC,CACrB,GAAG,aAAa,SAAS,CAAC,CAC1B,GAAG,SAAS,OAAO,CACnB,KAAK,OAAO;KACf,CAAC,OAAO,UAAU,IAAI,KAAK,mBAAmB,MAAM,CAAC,CAAC;AACxD,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,IAAI,SAAe,SAAS,WAAW;AAC3C,WAAO,MAAM,CACV,cAAc,CAAC,MAAM,YAAY,cAAc,CAAC,CAChD,WAAW,aAAa,CACxB,aAAa,KAAK,CAClB,GAAG,aAAa,SAAS,CAAC,CAC1B,GAAG,SAAS,OAAO,CACnB,KAAK,OAAO;KACf,CAAC,OAAO,UAAU,IAAI,KAAK,mBAAmB,MAAM,CAAC,CAAC;AAExD,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 { err, 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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m5kdev/backend",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Composable Express server stack with Drizzle ORM and tRPC.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
@@ -37,7 +37,6 @@
37
37
  "drizzle-zod": "0.8.2",
38
38
  "express": "4.21.2",
39
39
  "ffmpeg-ffprobe-static": "6.1.2-rc.1",
40
- "fluent-ffmpeg": "2.1.3",
41
40
  "ioredis": "5.7.0",
42
41
  "jsonrepair": "^3.13.3",
43
42
  "luxon": "3.7.1",
@@ -59,15 +58,14 @@
59
58
  "trpc-to-openapi": "2.3.0",
60
59
  "uuid": "11.0.5",
61
60
  "zod": "4.2.1",
62
- "@m5kdev/commons": "0.8.6",
63
- "@m5kdev/config": "0.8.6"
61
+ "@m5kdev/commons": "0.8.7",
62
+ "@m5kdev/config": "0.8.7"
64
63
  },
65
64
  "devDependencies": {
66
65
  "@jest/globals": "30.2.0",
67
66
  "@types/body-parser": "1.19.5",
68
67
  "@types/cors": "2.8.17",
69
68
  "@types/express": "4.17.21",
70
- "@types/fluent-ffmpeg": "2.1.27",
71
69
  "@types/jest": "30.0.0",
72
70
  "@types/luxon": "3.7.1",
73
71
  "@types/mustache": "4.2.6",