refactor services structure

This commit is contained in:
Maze Winther
2026-01-31 22:19:11 +01:00
parent 7878c8bb7b
commit f820d9a894
12 changed files with 779 additions and 778 deletions
+50 -45
View File
@@ -16,6 +16,16 @@ When implementing new features:
3. Understand the API surface of your codebase 3. Understand the API surface of your codebase
```typescript ```typescript
## apps/web/src/components/editor/timeline
audio-waveform.tsx
export function AudioWaveform({␍
audioUrl,␍
audioBuffer,␍
height = 32,␍
className = "",␍
}: AudioWaveformProps)
## apps/web/src/constants ## apps/web/src/constants
editor-constants.ts editor-constants.ts
@@ -574,7 +584,7 @@ definitions.ts
| "selection"␍ | "selection"␍
| "history"␍ | "history"␍
| "timeline"␍ | "timeline"␍
| "controls" | "con...
export interface TActionDefinition { export interface TActionDefinition {
description: string description: string
category: TActionCategory category: TActionCategory
@@ -604,7 +614,7 @@ registry.ts
types.ts types.ts
export type TActionArgsMap = {␍ export type TActionArgsMap = {␍
"seek-forward": { seconds: number } | undefined;␍ "seek-forward": { seconds: number } | undefined;␍
"seek-backward": { seconds: number } | undef... "seek-backward": { seconds: number } | und...
export type TActionWithArgs = keyof TActionArgsMap export type TActionWithArgs = keyof TActionArgsMap
export type TActionWithOptionalArgs = | TActionWithNoArgs␍ export type TActionWithOptionalArgs = | TActionWithNoArgs␍
| TKeysWithValueUndefined<TActionArgsMap> | TKeysWithValueUndefined<TActionArgsMap>
@@ -614,7 +624,7 @@ types.ts
: undefined : undefined
export type TActionFunc<A extends TAction> = A extends TActionWithArgs␍ export type TActionFunc<A extends TAction> = A extends TActionWithArgs␍
? (arg: TArgOfAction<A>, trigger?: TInvocationTrigger) => void␍ ? (arg: TArgOfAction<A>, trigger?: TInvocationTrigger) => void␍
: (_?:... : (_...
export type TInvocationTrigger = "keypress" | "mouseclick" export type TInvocationTrigger = "keypress" | "mouseclick"
export type TBoundActionList = {␍ export type TBoundActionList = {␍
[A in TAction]?: Array<TActionFunc<A>>;␍ [A in TAction]?: Array<TActionFunc<A>>;␍
@@ -673,12 +683,12 @@ parser.ts
export type GradientOrientation = LinearOrientation | Array<RadialOrientation> export type GradientOrientation = LinearOrientation | Array<RadialOrientation>
export type Color = | { type: "hex"; value: string }␍ export type Color = | { type: "hex"; value: string }␍
| { type: "literal"; value: string }␍ | { type: "literal"; value: string }␍
| { type: "rgb"; value: A... | { type: "rgb"; value:...
export type ColorStop = Color & { length?: Distance } export type ColorStop = Color & { length?: Distance }
export type GradientAst = {␍ export type GradientAst = {␍
type: GradientType;␍ type: GradientType;␍
orientation: GradientOrientation | undefined;␍ orientation: GradientOrientation | undefined;␍
colorStops: Array<ColorSto... colorStops: Array<Color...
export const parseGradient = ({␍ export const parseGradient = ({␍
code,␍ code,␍
}: {␍ }: {␍
@@ -691,8 +701,7 @@ parser.ts
audio.ts audio.ts
export type CollectedAudioElement = Omit<␍ export type CollectedAudioElement = Omit<␍
AudioElement,␍ AudioElement,␍
"type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceUrl" "type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceUrl...
...
export function createAudioContext(): AudioContext export function createAudioContext(): AudioContext
export interface DecodedAudio { export interface DecodedAudio {
samples: Float32Array samples: Float32Array
@@ -1119,27 +1128,6 @@ caption.ts
minDuration?: number;␍ minDuration?: number;␍
}): CaptionChunk[] }): CaptionChunk[]
## apps/web/src/services/media
video-cache.ts
export class VideoCache {
sinks
initPromises
async getFrameAt({
mediaId,
file,
time,
}: {
mediaId: string;
file: File;
time: number;
}): Promise<WrappedCanvas | null>
clearVideo({ mediaId }: { mediaId: string }): void
clearAll(): void
getStats()
}
export const videoCache
## apps/web/src/services/renderer ## apps/web/src/services/renderer
canvas-renderer.ts canvas-renderer.ts
@@ -1173,7 +1161,7 @@ scene-builder.ts
canvasSize: TCanvasSize;␍ canvasSize: TCanvasSize;␍
tracks: TimelineTrack[];␍ tracks: TimelineTrack[];␍
mediaAssets: MediaAsset[];␍ mediaAssets: MediaAsset[];␍
duration: numb... duration: ...
export function buildScene(params: BuildSceneParams) export function buildScene(params: BuildSceneParams)
scene-exporter.ts scene-exporter.ts
@@ -1183,7 +1171,7 @@ scene-exporter.ts
progress: [progress: number];␍ progress: [progress: number];␍
complete: [buffer: ArrayBuffer];␍ complete: [buffer: ArrayBuffer];␍
error: [error: Error];␍ error: [error: Error];␍
cance... c...
export class SceneExporter extends EventEmitter<SceneExporterEvents> { export class SceneExporter extends EventEmitter<SceneExporterEvents> {
renderer: CanvasRenderer renderer: CanvasRenderer
format: ExportFormat format: ExportFormat
@@ -1236,7 +1224,7 @@ opfs-adapter.ts
static isSupported(): boolean static isSupported(): boolean
} }
storage-service.ts service.ts
export const storageService export const storageService
types.ts types.ts
@@ -1270,11 +1258,10 @@ types.ts
"createdAt" | "updatedAt"␍ "createdAt" | "updatedAt"␍
> & {␍ > & {␍
createdAt: string;␍ createdAt: string;␍
updatedAt: string; updatedAt: st...
}
export type SerializedProject = Omit<TProject, "metadata" | "scenes"> & {␍ export type SerializedProject = Omit<TProject, "metadata" | "scenes"> & {␍
metadata: SerializedProjectMetadata;␍ metadata: SerializedProjectMetadata;␍
scenes: Serializ... scenes: Serial...
export interface StorageConfig { export interface StorageConfig {
projectsDb: string projectsDb: string
mediaDb: string mediaDb: string
@@ -1284,15 +1271,36 @@ types.ts
## apps/web/src/services/transcription ## apps/web/src/services/transcription
index.ts service.ts
export const transcriptionService export const transcriptionService
worker.ts worker.ts
export type WorkerMessage = | { type: "init"; modelId: string }␍ export type WorkerMessage = | { type: "init"; modelId: string }␍
| { type: "transcribe"; audio: Float32Array; language: strin... | { type: "transcribe"; audio: Float32Array; language: stri...
export type WorkerResponse = | { type: "init-progress"; progress: number }␍ export type WorkerResponse = | { type: "init-progress"; progress: number }␍
| { type: "init-complete" }␍ | { type: "init-complete" }␍
| { type: "init-error... | { type: "init-err...
## apps/web/src/services/video-cache
service.ts
export class VideoCache {
sinks
initPromises
async getFrameAt({␍
mediaId,␍
file,␍
time,␍
}: {␍
mediaId: string;␍
file: File;␍
time: number;␍
}): Promise<WrappedCanvas | null>
clearVideo({ mediaId }: { mediaId: string }): void
clearAll(): void
getStats()
}
export const videoCache
## apps/web/src/stores ## apps/web/src/stores
@@ -1360,13 +1368,13 @@ blog.ts
title: string;␍ title: string;␍
content: string;␍ content: string;␍
description: string;␍ description: string;␍
coverImage... cove...
export type Pagination = {␍ export type Pagination = {␍
limit: number;␍ limit: number;␍
currpage: number;␍ currpage: number;␍
nextPage: number | null;␍ nextPage: number | null;␍
prevPage: number | null;␍ prevPage: number | null;␍
totalIt... to...
export type MarblePostList = {␍ export type MarblePostList = {␍
posts: Post[];␍ posts: Post[];␍
pagination: Pagination;␍ pagination: Pagination;␍
@@ -1469,9 +1477,7 @@ keybinding.ts
| "j"␍ | "j"␍
| "k"␍ | "k"␍
| "l"␍ | "l"␍
| "m" |...
| "n"
...
export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}` export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}`
export type SingleCharacterShortcutKey = `${Key}` export type SingleCharacterShortcutKey = `${Key}`
export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey
@@ -1490,8 +1496,7 @@ project.ts
}␍ }␍
| {␍ | {␍
type: "blur";␍ type: "blur";␍
blurIntensity: number; blurIntensity: number...
}
export interface TCanvasSize { export interface TCanvasSize {
width: number width: number
height: number height: number
@@ -1679,7 +1684,7 @@ timeline.ts
| CreateVideoElement␍ | CreateVideoElement␍
| CreateImageElement␍ | CreateImageElement␍
| CreateTextElement␍ | CreateTextElement␍
| CreateSt... | Crea...
export interface ElementDragState { export interface ElementDragState {
isDragging: boolean isDragging: boolean
elementId: string | null elementId: string | null
@@ -17,7 +17,7 @@ import type {
TranscriptionLanguage, TranscriptionLanguage,
TranscriptionProgress, TranscriptionProgress,
} from "@/types/transcription"; } from "@/types/transcription";
import { transcriptionService } from "@/services/transcription"; import { transcriptionService } from "@/services/transcription/service";
import { decodeAudioToFloat32 } from "@/lib/media/audio"; import { decodeAudioToFloat32 } from "@/lib/media/audio";
import { buildCaptionChunks } from "@/lib/transcription/caption"; import { buildCaptionChunks } from "@/lib/transcription/caption";
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
+1 -1
View File
@@ -3,7 +3,7 @@
import { createContext, useContext, useEffect, useRef, useState } from "react"; import { createContext, useContext, useEffect, useRef, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { useEditor } from "@/hooks/use-editor"; import { useEditor } from "@/hooks/use-editor";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
interface StorageContextType { interface StorageContextType {
isInitialized: boolean; isInitialized: boolean;
+2 -2
View File
@@ -1,8 +1,8 @@
import type { EditorCore } from "@/core"; import type { EditorCore } from "@/core";
import type { MediaAsset } from "@/types/assets"; import type { MediaAsset } from "@/types/assets";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
import { generateUUID } from "@/utils/id"; import { generateUUID } from "@/utils/id";
import { videoCache } from "@/services/media/video-cache"; import { videoCache } from "@/services/video-cache/service";
import { hasMediaId } from "@/lib/timeline/element-utils"; import { hasMediaId } from "@/lib/timeline/element-utils";
export class MediaManager { export class MediaManager {
@@ -8,7 +8,7 @@ import type {
TTimelineViewState, TTimelineViewState,
} from "@/types/project"; } from "@/types/project";
import type { ExportOptions, ExportResult } from "@/types/export"; import type { ExportOptions, ExportResult } from "@/types/export";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
import { toast } from "sonner"; import { toast } from "sonner";
import { generateUUID } from "@/utils/id"; import { generateUUID } from "@/utils/id";
import { UpdateProjectSettingsCommand } from "@/lib/commands/project"; import { UpdateProjectSettingsCommand } from "@/lib/commands/project";
+1 -1
View File
@@ -1,6 +1,6 @@
import type { EditorCore } from "@/core"; import type { EditorCore } from "@/core";
import type { TimelineTrack, TScene } from "@/types/timeline"; import type { TimelineTrack, TScene } from "@/types/timeline";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
import { import {
getMainScene, getMainScene,
ensureMainScene, ensureMainScene,
@@ -2,7 +2,7 @@ import { Command } from "@/lib/commands/base-command";
import { EditorCore } from "@/core"; import { EditorCore } from "@/core";
import type { MediaAsset } from "@/types/assets"; import type { MediaAsset } from "@/types/assets";
import { generateUUID } from "@/utils/id"; import { generateUUID } from "@/utils/id";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
export class AddMediaAssetCommand extends Command { export class AddMediaAssetCommand extends Command {
private assetId: string; private assetId: string;
@@ -1,8 +1,8 @@
import { Command } from "@/lib/commands/base-command"; import { Command } from "@/lib/commands/base-command";
import { EditorCore } from "@/core"; import { EditorCore } from "@/core";
import type { MediaAsset } from "@/types/assets"; import type { MediaAsset } from "@/types/assets";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
import { videoCache } from "@/services/media/video-cache"; import { videoCache } from "@/services/video-cache/service";
import { hasMediaId } from "@/lib/timeline/element-utils"; import { hasMediaId } from "@/lib/timeline/element-utils";
import type { TimelineTrack } from "@/types/timeline"; import type { TimelineTrack } from "@/types/timeline";
@@ -333,12 +333,9 @@ class StorageService {
]); ]);
} }
// Utility methods
async clearAllData(): Promise<void> { async clearAllData(): Promise<void> {
// Clear all projects
await this.projectsAdapter.clear(); await this.projectsAdapter.clear();
// project-specific media and timelines cleaned up when projects are deleted
// Note: Project-specific media and timelines will be cleaned up when projects are deleted
} }
async getStorageInfo(): Promise<{ async getStorageInfo(): Promise<{
@@ -392,7 +389,6 @@ class StorageService {
try { try {
const currentData = await this.loadSavedSounds(); const currentData = await this.loadSavedSounds();
// Check if sound is already saved
if (currentData.sounds.some((sound) => sound.id === soundEffect.id)) { if (currentData.sounds.some((sound) => sound.id === soundEffect.id)) {
return; // Already saved return; // Already saved
} }
@@ -456,7 +452,6 @@ class StorageService {
} }
} }
// Check browser support
isOPFSSupported(): boolean { isOPFSSupported(): boolean {
return OPFSAdapter.isSupported(); return OPFSAdapter.isSupported();
} }
@@ -470,6 +465,5 @@ class StorageService {
} }
} }
// Export singleton instance
export const storageService = new StorageService(); export const storageService = new StorageService();
export { StorageService }; export { StorageService };
@@ -1,4 +1,5 @@
import type { import type {
TranscriptionLanguage,
TranscriptionResult, TranscriptionResult,
TranscriptionProgress, TranscriptionProgress,
TranscriptionModelId, TranscriptionModelId,
@@ -8,7 +9,6 @@ import {
TRANSCRIPTION_MODELS, TRANSCRIPTION_MODELS,
} from "@/constants/transcription-constants"; } from "@/constants/transcription-constants";
import type { WorkerMessage, WorkerResponse } from "./worker"; import type { WorkerMessage, WorkerResponse } from "./worker";
import type { LanguageCode } from "@/types/language";
type ProgressCallback = (progress: TranscriptionProgress) => void; type ProgressCallback = (progress: TranscriptionProgress) => void;
@@ -20,12 +20,12 @@ class TranscriptionService {
async transcribe({ async transcribe({
audioData, audioData,
language, language = "auto",
modelId = DEFAULT_TRANSCRIPTION_MODEL, modelId = DEFAULT_TRANSCRIPTION_MODEL,
onProgress, onProgress,
}: { }: {
audioData: Float32Array; audioData: Float32Array;
language?: LanguageCode; language?: TranscriptionLanguage;
modelId?: TranscriptionModelId; modelId?: TranscriptionModelId;
onProgress?: ProgressCallback; onProgress?: ProgressCallback;
}): Promise<TranscriptionResult> { }): Promise<TranscriptionResult> {
@@ -15,6 +15,7 @@ interface VideoSinkData {
prefetching: boolean; prefetching: boolean;
prefetchPromise: Promise<void> | null; prefetchPromise: Promise<void> | null;
} }
export class VideoCache { export class VideoCache {
private sinks = new Map<string, VideoSinkData>(); private sinks = new Map<string, VideoSinkData>();
private initPromises = new Map<string, Promise<void>>(); private initPromises = new Map<string, Promise<void>>();
@@ -311,4 +312,5 @@ export class VideoCache {
}; };
} }
} }
export const videoCache = new VideoCache(); export const videoCache = new VideoCache();
+1 -1
View File
@@ -1,6 +1,6 @@
import { create } from "zustand"; import { create } from "zustand";
import type { SoundEffect, SavedSound } from "@/types/sounds"; import type { SoundEffect, SavedSound } from "@/types/sounds";
import { storageService } from "@/services/storage/storage-service"; import { storageService } from "@/services/storage/service";
import { toast } from "sonner"; import { toast } from "sonner";
import { EditorCore } from "@/core"; import { EditorCore } from "@/core";
import { buildLibraryAudioElement } from "@/lib/timeline/element-utils"; import { buildLibraryAudioElement } from "@/lib/timeline/element-utils";