diff options
Diffstat (limited to 'apps/web/src/lib')
| -rw-r--r-- | apps/web/src/lib/auth-client.ts | 4 | ||||
| -rw-r--r-- | apps/web/src/lib/auth.ts | 23 | ||||
| -rw-r--r-- | apps/web/src/lib/db/index.tsx | 14 | ||||
| -rw-r--r-- | apps/web/src/lib/db/schema.ts | 69 | ||||
| -rw-r--r-- | apps/web/src/lib/fetchGhStars.ts | 29 | ||||
| -rw-r--r-- | apps/web/src/lib/ffmpeg-utils.ts | 64 |
6 files changed, 78 insertions, 125 deletions
diff --git a/apps/web/src/lib/auth-client.ts b/apps/web/src/lib/auth-client.ts deleted file mode 100644 index 7c467e7e..00000000 --- a/apps/web/src/lib/auth-client.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createAuthClient } from "better-auth/react";
-export const authClient = createAuthClient({
- baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL!,
-});
diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts deleted file mode 100644 index 4cfaa6f2..00000000 --- a/apps/web/src/lib/auth.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { betterAuth } from "better-auth";
-import { drizzleAdapter } from "better-auth/adapters/drizzle";
-import { db } from "./db";
-
-export const auth = betterAuth({
- database: drizzleAdapter(db, {
- provider: "pg",
- usePlural: true,
- }),
- secret: process.env.BETTER_AUTH_SECRET!,
- user: {
- deleteUser: {
- enabled: true,
- },
- },
- emailAndPassword: {
- enabled: true,
- },
- appName: "OpenCut",
- trustedOrigins: ["http://localhost:3000"],
-});
-
-export type Auth = typeof auth;
diff --git a/apps/web/src/lib/db/index.tsx b/apps/web/src/lib/db/index.tsx deleted file mode 100644 index b66c6e39..00000000 --- a/apps/web/src/lib/db/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { drizzle } from "drizzle-orm/node-postgres";
-import { Pool } from "pg";
-import * as schema from "./schema";
-
-const pool = new Pool({
- connectionString: process.env.DATABASE_URL,
- max: 3,
- idleTimeoutMillis: 10000,
- connectionTimeoutMillis: 15000,
- query_timeout: 20000,
- statement_timeout: 20000,
-});
-
-export const db = drizzle(pool, { schema });
diff --git a/apps/web/src/lib/db/schema.ts b/apps/web/src/lib/db/schema.ts deleted file mode 100644 index 7c38724a..00000000 --- a/apps/web/src/lib/db/schema.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
-
-export const users = pgTable("users", {
- id: text("id").primaryKey(),
- name: text("name").notNull(),
- email: text("email").notNull().unique(),
- emailVerified: boolean("email_verified")
- .$defaultFn(() => false)
- .notNull(),
- image: text("image"),
- createdAt: timestamp("created_at")
- .$defaultFn(() => /* @__PURE__ */ new Date())
- .notNull(),
- updatedAt: timestamp("updated_at")
- .$defaultFn(() => /* @__PURE__ */ new Date())
- .notNull(),
-}).enableRLS();
-
-export const sessions = pgTable("sessions", {
- id: text("id").primaryKey(),
- expiresAt: timestamp("expires_at").notNull(),
- token: text("token").notNull().unique(),
- createdAt: timestamp("created_at").notNull(),
- updatedAt: timestamp("updated_at").notNull(),
- ipAddress: text("ip_address"),
- userAgent: text("user_agent"),
- userId: text("user_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
-}).enableRLS();
-
-export const accounts = pgTable("accounts", {
- id: text("id").primaryKey(),
- accountId: text("account_id").notNull(),
- providerId: text("provider_id").notNull(),
- userId: text("user_id")
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- accessToken: text("access_token"),
- refreshToken: text("refresh_token"),
- idToken: text("id_token"),
- accessTokenExpiresAt: timestamp("access_token_expires_at"),
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
- scope: text("scope"),
- password: text("password"),
- createdAt: timestamp("created_at").notNull(),
- updatedAt: timestamp("updated_at").notNull(),
-}).enableRLS();
-
-export const verifications = pgTable("verifications", {
- id: text("id").primaryKey(),
- identifier: text("identifier").notNull(),
- value: text("value").notNull(),
- expiresAt: timestamp("expires_at").notNull(),
- createdAt: timestamp("created_at").$defaultFn(
- () => /* @__PURE__ */ new Date()
- ),
- updatedAt: timestamp("updated_at").$defaultFn(
- () => /* @__PURE__ */ new Date()
- ),
-}).enableRLS();
-
-export const waitlist = pgTable("waitlist", {
- id: text("id").primaryKey(),
- email: text("email").notNull().unique(),
- createdAt: timestamp("created_at")
- .$defaultFn(() => /* @__PURE__ */ new Date())
- .notNull(),
-}).enableRLS();
diff --git a/apps/web/src/lib/fetchGhStars.ts b/apps/web/src/lib/fetchGhStars.ts new file mode 100644 index 00000000..e0236840 --- /dev/null +++ b/apps/web/src/lib/fetchGhStars.ts @@ -0,0 +1,29 @@ +export async function getStars(): Promise<string> { + try { + const res = await fetch( + "https://api.github.com/repos/OpenCut-app/OpenCut", + { + next: { revalidate: 3600 }, + } + ); + + if (!res.ok) { + throw new Error(`GitHub API error: ${res.status} ${res.statusText}`); + } + const data = await res.json(); + const count = data.stargazers_count; + + if (typeof count !== "number") { + throw new Error("Invalid stargazers_count from GitHub API"); + } + + if (count >= 1_000_000) + return (count / 1_000_000).toFixed(1).replace(/\.0$/, "") + "M"; + if (count >= 1_000) + return (count / 1_000).toFixed(1).replace(/\.0$/, "") + "k"; + return count.toString(); + } catch (error) { + console.error("Failed to fetch GitHub stars:", error); + return "1.5k"; + } +} diff --git a/apps/web/src/lib/ffmpeg-utils.ts b/apps/web/src/lib/ffmpeg-utils.ts index 56085a21..ae0c267b 100644 --- a/apps/web/src/lib/ffmpeg-utils.ts +++ b/apps/web/src/lib/ffmpeg-utils.ts @@ -102,26 +102,60 @@ export const getVideoInfo = async (videoFile: File): Promise<{ fps: number; }> => { const ffmpeg = await initFFmpeg(); - + const inputName = 'input.mp4'; - + // Write input file await ffmpeg.writeFile(inputName, new Uint8Array(await videoFile.arrayBuffer())); - - // Get video info - await ffmpeg.exec(['-i', inputName, '-f', 'null', '-']); - - // Note: In a real implementation, you'd parse the FFmpeg output - // For now, we'll return default values and enhance this later - + + // Capture FFmpeg stderr output with a one-time listener pattern + let ffmpegOutput = ''; + let listening = true; + const listener = (data: string) => { + if (listening) ffmpegOutput += data; + }; + ffmpeg.on('log', ({ message }) => listener(message)); + + // Run ffmpeg to get info (stderr will contain the info) + try { + await ffmpeg.exec(['-i', inputName, '-f', 'null', '-']); + } catch (error) { + listening = false; + await ffmpeg.deleteFile(inputName); + console.error('FFmpeg execution failed:', error); + throw new Error('Failed to extract video info. The file may be corrupted or in an unsupported format.'); + } + + // Disable listener after exec completes + listening = false; + // Cleanup await ffmpeg.deleteFile(inputName); - + + // Parse output for duration, resolution, and fps + // Example: Duration: 00:00:10.00, start: 0.000000, bitrate: 1234 kb/s + // Example: Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 30 fps, 30 tbr, 90k tbn, 60 tbc + + const durationMatch = ffmpegOutput.match(/Duration: (\d+):(\d+):([\d.]+)/); + let duration = 0; + if (durationMatch) { + const [, h, m, s] = durationMatch; + duration = parseInt(h) * 3600 + parseInt(m) * 60 + parseFloat(s); + } + + const videoStreamMatch = ffmpegOutput.match(/Video:.* (\d+)x(\d+)[^,]*, ([\d.]+) fps/); + let width = 0, height = 0, fps = 0; + if (videoStreamMatch) { + width = parseInt(videoStreamMatch[1]); + height = parseInt(videoStreamMatch[2]); + fps = parseFloat(videoStreamMatch[3]); + } + return { - duration: 10, // Placeholder - would parse from FFmpeg output - width: 1920, // Placeholder - height: 1080, // Placeholder - fps: 30 // Placeholder + duration, + width, + height, + fps }; }; @@ -194,4 +228,4 @@ export const extractAudio = async ( await ffmpeg.deleteFile(outputName); return blob; -};
\ No newline at end of file +};
\ No newline at end of file |
