summaryrefslogtreecommitdiff
path: root/apps/web/src/lib
diff options
context:
space:
mode:
authorenkei64 <imenkei64@gmail.com>2025-08-04 18:11:24 +1000
committerGitHub <noreply@github.com>2025-08-04 18:11:24 +1000
commit21b248718b0d1f6e7f32a8bac0cc1277241b7bd5 (patch)
tree70d9c53fc561a9100dd4c7d1cb6d49d45e35b989 /apps/web/src/lib
parentbb36b2e439bffcaa7059e3c0578a0fc61c2737ac (diff)
parentd022a78ae2330665e7d763fdc6e831a71f3b9ea1 (diff)
downloadopencut-21b248718b0d1f6e7f32a8bac0cc1277241b7bd5.tar.gz
Merge branch 'staging' into feature/panel-presets
Diffstat (limited to 'apps/web/src/lib')
-rw-r--r--apps/web/src/lib/rate-limit.ts6
-rw-r--r--apps/web/src/lib/storage/storage-service.ts92
-rw-r--r--apps/web/src/lib/storage/types.ts1
-rw-r--r--apps/web/src/lib/waitlist.ts13
4 files changed, 96 insertions, 16 deletions
diff --git a/apps/web/src/lib/rate-limit.ts b/apps/web/src/lib/rate-limit.ts
index e1996cf5..92f70805 100644
--- a/apps/web/src/lib/rate-limit.ts
+++ b/apps/web/src/lib/rate-limit.ts
@@ -8,9 +8,9 @@ const redis = new Redis({
token: env.UPSTASH_REDIS_REST_TOKEN,
});
-export const waitlistRateLimit = new Ratelimit({
+export const baseRateLimit = new Ratelimit({
redis,
- limiter: Ratelimit.slidingWindow(5, "1 m"), // 5 requests per minute
+ limiter: Ratelimit.slidingWindow(100, "1 m"), // 100 requests per minute
analytics: true,
- prefix: "waitlist-rate-limit",
+ prefix: "rate-limit",
});
diff --git a/apps/web/src/lib/storage/storage-service.ts b/apps/web/src/lib/storage/storage-service.ts
index 2ff63ac7..98001591 100644
--- a/apps/web/src/lib/storage/storage-service.ts
+++ b/apps/web/src/lib/storage/storage-service.ts
@@ -9,9 +9,11 @@ import {
TimelineData,
} from "./types";
import { TimelineTrack } from "@/types/timeline";
+import { SavedSoundsData, SavedSound, SoundEffect } from "@/types/sounds";
class StorageService {
private projectsAdapter: IndexedDBAdapter<SerializedProject>;
+ private savedSoundsAdapter: IndexedDBAdapter<SavedSoundsData>;
private config: StorageConfig;
constructor() {
@@ -19,6 +21,7 @@ class StorageService {
projectsDb: "video-editor-projects",
mediaDb: "video-editor-media",
timelineDb: "video-editor-timelines",
+ savedSoundsDb: "video-editor-saved-sounds",
version: 1,
};
@@ -27,6 +30,12 @@ class StorageService {
"projects",
this.config.version
);
+
+ this.savedSoundsAdapter = new IndexedDBAdapter<SavedSoundsData>(
+ this.config.savedSoundsDb,
+ "saved-sounds",
+ this.config.version
+ );
}
// Helper to get project-specific media adapters
@@ -264,6 +273,89 @@ class StorageService {
};
}
+ async loadSavedSounds(): Promise<SavedSoundsData> {
+ try {
+ const savedSoundsData = await this.savedSoundsAdapter.get("user-sounds");
+ return (
+ savedSoundsData || {
+ sounds: [],
+ lastModified: new Date().toISOString(),
+ }
+ );
+ } catch (error) {
+ console.error("Failed to load saved sounds:", error);
+ return { sounds: [], lastModified: new Date().toISOString() };
+ }
+ }
+
+ async saveSoundEffect(soundEffect: SoundEffect): Promise<void> {
+ try {
+ const currentData = await this.loadSavedSounds();
+
+ // Check if sound is already saved
+ if (currentData.sounds.some((sound) => sound.id === soundEffect.id)) {
+ return; // Already saved
+ }
+
+ const savedSound: SavedSound = {
+ id: soundEffect.id,
+ name: soundEffect.name,
+ username: soundEffect.username,
+ previewUrl: soundEffect.previewUrl,
+ downloadUrl: soundEffect.downloadUrl,
+ duration: soundEffect.duration,
+ tags: soundEffect.tags,
+ license: soundEffect.license,
+ savedAt: new Date().toISOString(),
+ };
+
+ const updatedData: SavedSoundsData = {
+ sounds: [...currentData.sounds, savedSound],
+ lastModified: new Date().toISOString(),
+ };
+
+ await this.savedSoundsAdapter.set("user-sounds", updatedData);
+ } catch (error) {
+ console.error("Failed to save sound effect:", error);
+ throw error;
+ }
+ }
+
+ async removeSavedSound(soundId: number): Promise<void> {
+ try {
+ const currentData = await this.loadSavedSounds();
+
+ const updatedData: SavedSoundsData = {
+ sounds: currentData.sounds.filter((sound) => sound.id !== soundId),
+ lastModified: new Date().toISOString(),
+ };
+
+ await this.savedSoundsAdapter.set("user-sounds", updatedData);
+ } catch (error) {
+ console.error("Failed to remove saved sound:", error);
+ throw error;
+ }
+ }
+
+ async isSoundSaved(soundId: number): Promise<boolean> {
+ try {
+ const currentData = await this.loadSavedSounds();
+ return currentData.sounds.some((sound) => sound.id === soundId);
+ } catch (error) {
+ console.error("Failed to check if sound is saved:", error);
+ return false;
+ }
+ }
+
+ async clearSavedSounds(): Promise<void> {
+ try {
+ await this.savedSoundsAdapter.remove("user-sounds");
+ } catch (error) {
+ console.error("Failed to clear saved sounds:", error);
+ throw error;
+ }
+ }
+
// Check browser support
isOPFSSupported(): boolean {
return OPFSAdapter.isSupported();
diff --git a/apps/web/src/lib/storage/types.ts b/apps/web/src/lib/storage/types.ts
index 0cfafd47..127b1690 100644
--- a/apps/web/src/lib/storage/types.ts
+++ b/apps/web/src/lib/storage/types.ts
@@ -30,6 +30,7 @@ export interface StorageConfig {
projectsDb: string;
mediaDb: string;
timelineDb: string;
+ savedSoundsDb: string;
version: number;
}
diff --git a/apps/web/src/lib/waitlist.ts b/apps/web/src/lib/waitlist.ts
deleted file mode 100644
index 43a620b0..00000000
--- a/apps/web/src/lib/waitlist.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { db, sql, waitlist } from "@opencut/db";
-
-export async function getWaitlistCount() {
- try {
- const result = await db
- .select({ count: sql<number>`count(*)` })
- .from(waitlist);
- return result[0]?.count || 0;
- } catch (error) {
- console.error("Failed to fetch waitlist count:", error);
- return 0;
- }
-}