melies-video-editor 0.1.3 → 0.1.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.
@@ -1,9 +1,30 @@
1
1
  type MediaKind = 'video' | 'audio' | 'other';
2
+ export type ProxyEvent = {
3
+ type: 'proxy-start';
4
+ originalSrc: string;
5
+ } | {
6
+ type: 'proxy-progress';
7
+ originalSrc: string;
8
+ value?: number;
9
+ status?: string;
10
+ } | {
11
+ type: 'proxy-ready';
12
+ originalSrc: string;
13
+ proxySrc: string;
14
+ } | {
15
+ type: 'proxy-failed';
16
+ originalSrc: string;
17
+ error: string;
18
+ };
2
19
  declare const guessKind: (src: string) => MediaKind;
3
20
  declare class MediaCache {
4
21
  private blobUrlBySrc;
5
22
  private pendingBySrc;
6
23
  private metaBySrc;
24
+ private proxyEventListeners;
25
+ /** Subscribe to proxy generation events (useful for UI debug on mobile where console logs aren't visible). */
26
+ onProxyEvent(listener: (event: ProxyEvent) => void): () => void;
27
+ private emitProxyEvent;
7
28
  registerSrcMeta(src: string, meta: {
8
29
  name?: string;
9
30
  mimeType?: string;
@@ -12,6 +33,24 @@ declare class MediaCache {
12
33
  name?: string;
13
34
  mimeType?: string;
14
35
  } | undefined;
36
+ /**
37
+ * Ingests a File object into OPFS.
38
+ * Returns a virtual 'opfs://' URL that can be used as a src reference.
39
+ */
40
+ ingest(file: File): Promise<string>;
41
+ private initProxy;
42
+ /**
43
+ * Returns a URL that is safe to assign to a media element (`<video>`/`<audio>`).
44
+ *
45
+ * Notes:
46
+ * - `opfs://...` is NOT directly playable by browsers.
47
+ * - For OPFS sources, this will kick off an async preload (creating a `blob:` URL) and return `undefined`.
48
+ */
49
+ resolveForMediaElement(src: string): string | undefined;
50
+ /**
51
+ * Helper to fetch a file from OPFS given an opfs:// URL
52
+ */
53
+ getFileFromOpfs(opfsUrl: string): Promise<File | undefined>;
15
54
  /**
16
55
  * Preloads a URL into memory and returns a blob: URL.
17
56
  * Useful to avoid runtime buffering/stalls when seeking frequently.
@@ -1,23 +1,37 @@
1
- type OpfsRoot = FileSystemDirectoryHandle;
2
- type Maybe<T> = T | null | undefined;
1
+ export type OpfsRoot = FileSystemDirectoryHandle;
3
2
  export declare const ensureOpfsRoot: () => Promise<OpfsRoot>;
4
3
  export declare const ensureDir: (root: OpfsRoot, path: string) => Promise<FileSystemDirectoryHandle>;
5
- export declare const writeBlobToOpfs: (opts: {
4
+ /**
5
+ * Writes a Blob or File to OPFS.
6
+ * Uses createWritable() which is the standard async way to write.
7
+ */
8
+ export declare const writeFileToOpfs: (opts: {
6
9
  root: OpfsRoot;
7
10
  dirPath: string;
8
11
  fileName: string;
9
- blob: Blob;
12
+ file: Blob | File;
10
13
  }) => Promise<{
11
14
  fileHandle: FileSystemFileHandle;
12
15
  path: string;
13
16
  }>;
17
+ export declare const getOpfsFile: (path: string) => Promise<File | undefined>;
18
+ export declare const listFiles: (opts: {
19
+ root: OpfsRoot;
20
+ dirPath: string;
21
+ }) => Promise<FileSystemFileHandle[]>;
14
22
  export declare const clearDir: (opts: {
15
23
  root: OpfsRoot;
16
24
  dirPath: string;
17
25
  }) => Promise<void>;
18
- export declare const listFiles: (opts: {
26
+ type Maybe<T> = T | null | undefined;
27
+ export declare const getFileFromPublicUrl: (url: string, fallbackName?: Maybe<string>) => Promise<File>;
28
+ export declare const writeBlobToOpfs: (opts: {
19
29
  root: OpfsRoot;
20
30
  dirPath: string;
21
- }) => Promise<FileSystemFileHandle[]>;
22
- export declare const getFileFromPublicUrl: (url: string, fallbackName?: Maybe<string>) => Promise<File>;
31
+ fileName: string;
32
+ blob: Blob;
33
+ }) => Promise<{
34
+ fileHandle: FileSystemFileHandle;
35
+ path: string;
36
+ }>;
23
37
  export {};
@@ -0,0 +1,19 @@
1
+ declare class ProxyManager {
2
+ private activeJobs;
3
+ /**
4
+ * Create (or reuse) a proxy file for an OPFS video.
5
+ *
6
+ * `originalOpfsPath` should be an `opfs://...` URL.
7
+ */
8
+ createProxy(originalOpfsPath: string, opts?: {
9
+ onProgress?: (info: {
10
+ value?: number;
11
+ status?: string;
12
+ }) => void;
13
+ }): Promise<string>;
14
+ private _runWorker;
15
+ private getHandle;
16
+ private ensureDir;
17
+ }
18
+ export declare const proxyManager: ProxyManager;
19
+ export {};
@@ -1,44 +1,71 @@
1
- import type { TimelineEngine } from '@xzdarcy/react-timeline-editor';
1
+ import type { TimelineEngine, TimelineRow } from '@xzdarcy/react-timeline-editor';
2
2
  declare class VideoControl {
3
- private videoEl;
4
- private currentSrc;
5
- private lastSeekAtMs;
6
- private lastRate;
7
- private videoClaims;
8
- private activeVideoActionId;
9
- private lastEngineTime;
3
+ private primaryEl;
4
+ private secondaryEl;
5
+ private activeEl;
6
+ private rowData;
10
7
  private boundEngine;
11
- private boundActionStart;
12
8
  private vfcHandle;
13
9
  private rafHandle;
10
+ private isPlaying;
11
+ private playbackRate;
12
+ private lastVideoClip;
13
+ private lastKnownTime;
14
+ constructor();
15
+ /**
16
+ * Called by App.tsx to provide the two video elements.
17
+ */
18
+ attachPrimary(el: HTMLVideoElement | null): void;
19
+ attachSecondary(el: HTMLVideoElement | null): void;
20
+ /**
21
+ * Helper to set initial styling/events for video elements.
22
+ */
23
+ private initElement;
24
+ /**
25
+ * Deprecated single-element attach, mapped to primary for safety.
26
+ */
14
27
  attach(el: HTMLVideoElement | null): void;
15
- setActive(active: boolean): void;
28
+ /**
29
+ * Called by App.tsx whenever timeline data changes.
30
+ */
31
+ setEditorData(data: TimelineRow[]): void;
32
+ /**
33
+ * We use claimVideo to drive the dual-buffer logic from the engine's time updates.
34
+ * This ensures we sync to the engine's clock (Slave Mode).
35
+ */
16
36
  claimVideo(data: {
17
- actionId: string;
18
- layer: number;
19
- src: string;
20
- engine: TimelineEngine;
21
- isPlaying: boolean;
22
- time: number;
23
- actionStart: number;
24
- offset?: number;
37
+ isPlaying?: boolean;
38
+ time?: number;
39
+ [key: string]: unknown;
25
40
  }): void;
26
- releaseVideo(actionIdRaw: string): void;
27
- bindEngine(engine: TimelineEngine, actionStart: number): void;
41
+ bindEngine(engine: TimelineEngine): void;
28
42
  unbindEngine(): void;
29
- private tickFromVideo;
30
- setSource(src: string): void;
31
- warm(src: string): void;
32
- setRate(rate: number): void;
43
+ private startLoop;
44
+ private stopLoop;
33
45
  /**
34
- * Sync the video to a desired timeline time.
35
- * To avoid buffering/stutters, we only seek when drift is large or when paused/scrubbing.
46
+ * The main heart beat.
47
+ * - If playing: sync engine time to video time.
48
+ * - Always: Check schedule (what should be playing vs preloading).
36
49
  */
37
- seek(time: number, opts?: {
38
- force?: boolean;
39
- }): void;
40
- play(): Promise<void>;
50
+ private tickLoop;
51
+ /**
52
+ * Evaluate the timeline at `time` (and `time + lookahead`).
53
+ * Manage Primary/Secondary elements.
54
+ */
55
+ private updateState;
56
+ private makeActive;
57
+ private loadVideo;
58
+ private isLoaded;
59
+ private findClipAtTime;
60
+ private findNextVideoClipStartAfter;
61
+ private findNextVideoClipAfter;
62
+ play(): void;
41
63
  pause(): void;
64
+ setRate(rate: number): void;
65
+ seek(time: number): void;
66
+ warm(src: string): void;
67
+ releaseVideo(_actionId: string): void;
68
+ setActive(_active: boolean): void;
42
69
  }
43
70
  declare const _default: VideoControl;
44
71
  export default _default;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,74 +1,74 @@
1
- {
2
- "name": "melies-video-editor",
3
- "version": "0.1.3",
4
- "description": "A React video timeline editor GUI built on @xzdarcy/react-timeline-editor.",
5
- "type": "module",
6
- "license": "MIT",
7
- "keywords": [
8
- "timeline",
9
- "video",
10
- "editor",
11
- "react",
12
- "base44"
13
- ],
14
- "main": "./dist/index.cjs",
15
- "module": "./dist/index.js",
16
- "types": "./dist/types/lib/index.d.ts",
17
- "exports": {
18
- ".": {
19
- "types": "./dist/types/lib/index.d.ts",
20
- "import": "./dist/index.js",
21
- "require": "./dist/index.cjs"
22
- },
23
- "./style.css": "./dist/style.css"
24
- },
25
- "files": [
26
- "dist",
27
- "README.md",
28
- "LICENSE"
29
- ],
30
- "sideEffects": [
31
- "**/*.css"
32
- ],
33
- "scripts": {
34
- "dev": "vite --host",
35
- "dev:server": "node server/index.mjs",
36
- "build": "tsc -b && vite build && tsc -p tsconfig.types.json",
37
- "lint": "eslint .",
38
- "preview": "vite preview"
39
- },
40
- "dependencies": {
41
- "@ant-design/icons": "4.7.0",
42
- "@dnd-kit/core": "^6.3.1",
43
- "@xzdarcy/react-timeline-editor": "0.1.8",
44
- "antd": "4.21.6",
45
- "howler": "2.2.3",
46
- "lodash": "4.17.21",
47
- "lottie-web": "5.10.0"
48
- },
49
- "peerDependencies": {
50
- "react": "^18.0.0",
51
- "react-dom": "^18.0.0"
52
- },
53
- "devDependencies": {
54
- "@eslint/js": "^9.39.1",
55
- "@types/node": "^24.10.1",
56
- "@types/howler": "^2.2.12",
57
- "@types/react": "^18.3.18",
58
- "@types/react-dom": "^18.3.5",
59
- "@vitejs/plugin-react": "^5.1.1",
60
- "eslint": "^9.39.1",
61
- "eslint-plugin-react-hooks": "^7.0.1",
62
- "eslint-plugin-react-refresh": "^0.4.24",
63
- "express": "^4.21.2",
64
- "globals": "^16.5.0",
65
- "less": "^4.5.1",
66
- "multer": "^2.0.2",
67
- "react": "^18.2.0",
68
- "react-dom": "^18.2.0",
69
- "typescript": "~5.9.3",
70
- "typescript-eslint": "^8.46.4",
71
- "vite": "^7.2.4"
72
- },
73
- "packageManager": "pnpm@8.7.1+sha1.cb9fb56ca170a718619cd9bc669d99ddadc1afb5"
74
- }
1
+ {
2
+ "name": "melies-video-editor",
3
+ "version": "0.1.4",
4
+ "description": "A React video timeline editor GUI built on @xzdarcy/react-timeline-editor.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "timeline",
9
+ "video",
10
+ "editor",
11
+ "react",
12
+ "base44"
13
+ ],
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/types/lib/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/types/lib/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs"
22
+ },
23
+ "./style.css": "./dist/style.css"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "sideEffects": [
31
+ "**/*.css"
32
+ ],
33
+ "scripts": {
34
+ "dev": "vite --host",
35
+ "dev:server": "node server/index.mjs",
36
+ "build": "tsc -b && vite build && tsc -p tsconfig.types.json",
37
+ "lint": "eslint .",
38
+ "preview": "vite preview"
39
+ },
40
+ "dependencies": {
41
+ "@ant-design/icons": "4.7.0",
42
+ "@dnd-kit/core": "^6.3.1",
43
+ "@xzdarcy/react-timeline-editor": "0.1.8",
44
+ "antd": "4.21.6",
45
+ "howler": "2.2.3",
46
+ "lodash": "4.17.21",
47
+ "lottie-web": "5.10.0"
48
+ },
49
+ "peerDependencies": {
50
+ "react": "^18.0.0",
51
+ "react-dom": "^18.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/js": "^9.39.1",
55
+ "@types/node": "^24.10.1",
56
+ "@types/howler": "^2.2.12",
57
+ "@types/react": "^18.3.18",
58
+ "@types/react-dom": "^18.3.5",
59
+ "@vitejs/plugin-react": "^5.1.1",
60
+ "eslint": "^9.39.1",
61
+ "eslint-plugin-react-hooks": "^7.0.1",
62
+ "eslint-plugin-react-refresh": "^0.4.24",
63
+ "express": "^4.21.2",
64
+ "globals": "^16.5.0",
65
+ "less": "^4.5.1",
66
+ "multer": "^2.0.2",
67
+ "react": "^18.2.0",
68
+ "react-dom": "^18.2.0",
69
+ "typescript": "~5.9.3",
70
+ "typescript-eslint": "^8.46.4",
71
+ "vite": "^7.2.4"
72
+ },
73
+ "packageManager": "pnpm@8.7.1+sha1.cb9fb56ca170a718619cd9bc669d99ddadc1afb5"
74
+ }