reduce installer size 50% and app bundle size by 30%
@@ -6,12 +6,19 @@
|
||||
"productName": "Openscreen",
|
||||
"npmRebuild": true,
|
||||
"buildDependenciesFromSource": true,
|
||||
"compression": "maximum",
|
||||
"directories": {
|
||||
"output": "release/${version}"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"dist-electron"
|
||||
"dist-electron",
|
||||
"!*.png",
|
||||
"!preview*.png",
|
||||
"!*.md",
|
||||
"!README.md",
|
||||
"!CONTRIBUTING.md",
|
||||
"!LICENSE"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
@@ -19,6 +26,9 @@
|
||||
"to": "assets/wallpapers"
|
||||
}
|
||||
],
|
||||
"asarUnpack": [
|
||||
"**/node_modules/uiohook-napi/**/*"
|
||||
],
|
||||
"mac": {
|
||||
"target": [
|
||||
"dmg"
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"terser": "^5.44.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.6",
|
||||
"vite-plugin-electron": "^0.28.6",
|
||||
@@ -1447,6 +1448,17 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
@@ -9832,6 +9844,32 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.44.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
|
||||
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"terser": "^5.44.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.6",
|
||||
"vite-plugin-electron": "^0.28.6",
|
||||
|
||||
|
Before Width: | Height: | Size: 20 MiB |
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 682 KiB |
|
Before Width: | Height: | Size: 12 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 15 MiB After Width: | Height: | Size: 827 KiB |
|
Before Width: | Height: | Size: 18 MiB After Width: | Height: | Size: 524 KiB |
|
Before Width: | Height: | Size: 20 MiB After Width: | Height: | Size: 538 KiB |
|
Before Width: | Height: | Size: 8.5 MiB After Width: | Height: | Size: 897 KiB |
@@ -1,7 +1,7 @@
|
||||
import type React from "react";
|
||||
import { useEffect, useRef, useImperativeHandle, forwardRef, useState, useMemo, useCallback } from "react";
|
||||
import { getAssetPath } from "@/lib/assetPath";
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { Application, Container, Sprite, Graphics, BlurFilter, Texture, VideoSource } from 'pixi.js';
|
||||
import { ZOOM_DEPTH_SCALES, type ZoomRegion, type ZoomFocus, type ZoomDepth } from "./types";
|
||||
import { DEFAULT_FOCUS, SMOOTHING_FACTOR, MIN_DELTA } from "./videoPlayback/constants";
|
||||
import { clamp01 } from "./videoPlayback/mathUtils";
|
||||
@@ -31,9 +31,9 @@ interface VideoPlaybackProps {
|
||||
|
||||
export interface VideoPlaybackRef {
|
||||
video: HTMLVideoElement | null;
|
||||
app: PIXI.Application | null;
|
||||
videoSprite: PIXI.Sprite | null;
|
||||
videoContainer: PIXI.Container | null;
|
||||
app: Application | null;
|
||||
videoSprite: Sprite | null;
|
||||
videoContainer: Container | null;
|
||||
play: () => Promise<void>;
|
||||
pause: () => void;
|
||||
}
|
||||
@@ -56,10 +56,10 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
}, ref) => {
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const appRef = useRef<PIXI.Application | null>(null);
|
||||
const videoSpriteRef = useRef<PIXI.Sprite | null>(null);
|
||||
const videoContainerRef = useRef<PIXI.Container | null>(null);
|
||||
const cameraContainerRef = useRef<PIXI.Container | null>(null);
|
||||
const appRef = useRef<Application | null>(null);
|
||||
const videoSpriteRef = useRef<Sprite | null>(null);
|
||||
const videoContainerRef = useRef<Container | null>(null);
|
||||
const cameraContainerRef = useRef<Container | null>(null);
|
||||
const timeUpdateAnimationRef = useRef<number | null>(null);
|
||||
const [pixiReady, setPixiReady] = useState(false);
|
||||
const [videoReady, setVideoReady] = useState(false);
|
||||
@@ -69,7 +69,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
const zoomRegionsRef = useRef<ZoomRegion[]>([]);
|
||||
const selectedZoomIdRef = useRef<string | null>(null);
|
||||
const animationStateRef = useRef({ scale: 1, focusX: DEFAULT_FOCUS.cx, focusY: DEFAULT_FOCUS.cy });
|
||||
const blurFilterRef = useRef<PIXI.BlurFilter | null>(null);
|
||||
const blurFilterRef = useRef<BlurFilter | null>(null);
|
||||
const isDraggingFocusRef = useRef(false);
|
||||
const stageSizeRef = useRef({ width: 0, height: 0 });
|
||||
const videoSizeRef = useRef({ width: 0, height: 0 });
|
||||
@@ -77,7 +77,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
const baseOffsetRef = useRef({ x: 0, y: 0 });
|
||||
const baseMaskRef = useRef({ x: 0, y: 0, width: 0, height: 0 });
|
||||
const cropBoundsRef = useRef({ startX: 0, endX: 0, startY: 0, endY: 0 });
|
||||
const maskGraphicsRef = useRef<PIXI.Graphics | null>(null);
|
||||
const maskGraphicsRef = useRef<Graphics | null>(null);
|
||||
const isPlayingRef = useRef(isPlaying);
|
||||
const isSeekingRef = useRef(false);
|
||||
const allowPlaybackRef = useRef(false);
|
||||
@@ -398,10 +398,10 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
if (!container) return;
|
||||
|
||||
let mounted = true;
|
||||
let app: PIXI.Application | null = null;
|
||||
let app: Application | null = null;
|
||||
|
||||
(async () => {
|
||||
app = new PIXI.Application();
|
||||
app = new Application();
|
||||
|
||||
await app.init({
|
||||
width: container.clientWidth,
|
||||
@@ -423,12 +423,12 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
container.appendChild(app.canvas);
|
||||
|
||||
// Camera container - this will be scaled/positioned for zoom
|
||||
const cameraContainer = new PIXI.Container();
|
||||
const cameraContainer = new Container();
|
||||
cameraContainerRef.current = cameraContainer;
|
||||
app.stage.addChild(cameraContainer);
|
||||
|
||||
// Video container - holds the masked video sprite
|
||||
const videoContainer = new PIXI.Container();
|
||||
const videoContainer = new Container();
|
||||
videoContainerRef.current = videoContainer;
|
||||
cameraContainer.addChild(videoContainer);
|
||||
|
||||
@@ -468,19 +468,19 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
if (!video || !app || !videoContainer) return;
|
||||
if (video.videoWidth === 0 || video.videoHeight === 0) return;
|
||||
|
||||
const source = PIXI.VideoSource.from(video);
|
||||
const source = VideoSource.from(video);
|
||||
if ('autoPlay' in source) {
|
||||
(source as { autoPlay?: boolean }).autoPlay = false;
|
||||
}
|
||||
if ('autoUpdate' in source) {
|
||||
(source as { autoUpdate?: boolean }).autoUpdate = true;
|
||||
}
|
||||
const videoTexture = PIXI.Texture.from(source);
|
||||
const videoTexture = Texture.from(source);
|
||||
|
||||
const videoSprite = new PIXI.Sprite(videoTexture);
|
||||
const videoSprite = new Sprite(videoTexture);
|
||||
videoSpriteRef.current = videoSprite;
|
||||
|
||||
const maskGraphics = new PIXI.Graphics();
|
||||
const maskGraphics = new Graphics();
|
||||
videoContainer.addChild(videoSprite);
|
||||
videoContainer.addChild(maskGraphics);
|
||||
videoContainer.mask = maskGraphics;
|
||||
@@ -492,7 +492,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
|
||||
focusY: DEFAULT_FOCUS.cy,
|
||||
};
|
||||
|
||||
const blurFilter = new PIXI.BlurFilter();
|
||||
const blurFilter = new BlurFilter();
|
||||
blurFilter.quality = 3;
|
||||
blurFilter.resolution = app.renderer.resolution;
|
||||
blurFilter.blur = 0;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { Application, Sprite, Graphics } from 'pixi.js';
|
||||
import { VIEWPORT_SCALE } from "./constants";
|
||||
import type { CropRegion } from '../types';
|
||||
|
||||
interface LayoutParams {
|
||||
container: HTMLDivElement;
|
||||
app: PIXI.Application;
|
||||
videoSprite: PIXI.Sprite;
|
||||
maskGraphics: PIXI.Graphics;
|
||||
app: Application;
|
||||
videoSprite: Sprite;
|
||||
maskGraphics: Graphics;
|
||||
videoElement: HTMLVideoElement;
|
||||
cropRegion?: CropRegion;
|
||||
lockedVideoDimensions?: { width: number; height: number } | null;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { Container, BlurFilter } from 'pixi.js';
|
||||
|
||||
interface TransformParams {
|
||||
cameraContainer: PIXI.Container;
|
||||
blurFilter: PIXI.BlurFilter | null;
|
||||
cameraContainer: Container;
|
||||
blurFilter: BlurFilter | null;
|
||||
stageSize: { width: number; height: number };
|
||||
baseMask: { x: number; y: number; width: number; height: number };
|
||||
zoomScale: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import { Application, Container, Sprite, Graphics, BlurFilter, Texture } from 'pixi.js';
|
||||
import type { ZoomRegion, CropRegion } from '@/components/video-editor/types';
|
||||
import { ZOOM_DEPTH_SCALES } from '@/components/video-editor/types';
|
||||
import { findDominantRegion } from '@/components/video-editor/videoPlayback/zoomRegionUtils';
|
||||
@@ -27,13 +27,13 @@ interface AnimationState {
|
||||
// Renders video frames with all effects (background, zoom, crop, blur, shadow) to an offscreen canvas for export.
|
||||
|
||||
export class FrameRenderer {
|
||||
private app: PIXI.Application | null = null;
|
||||
private cameraContainer: PIXI.Container | null = null;
|
||||
private videoContainer: PIXI.Container | null = null;
|
||||
private videoSprite: PIXI.Sprite | null = null;
|
||||
private backgroundSprite: PIXI.Sprite | null = null;
|
||||
private maskGraphics: PIXI.Graphics | null = null;
|
||||
private blurFilter: PIXI.BlurFilter | null = null;
|
||||
private app: Application | null = null;
|
||||
private cameraContainer: Container | null = null;
|
||||
private videoContainer: Container | null = null;
|
||||
private videoSprite: Sprite | null = null;
|
||||
private backgroundSprite: Sprite | null = null;
|
||||
private maskGraphics: Graphics | null = null;
|
||||
private blurFilter: BlurFilter | null = null;
|
||||
private shadowCanvas: HTMLCanvasElement | null = null;
|
||||
private shadowCtx: CanvasRenderingContext2D | null = null;
|
||||
private compositeCanvas: HTMLCanvasElement | null = null;
|
||||
@@ -70,7 +70,7 @@ export class FrameRenderer {
|
||||
}
|
||||
|
||||
// Initialize PixiJS with optimized settings for export performance
|
||||
this.app = new PIXI.Application();
|
||||
this.app = new Application();
|
||||
await this.app.init({
|
||||
canvas,
|
||||
width: this.config.width,
|
||||
@@ -82,8 +82,8 @@ export class FrameRenderer {
|
||||
});
|
||||
|
||||
// Setup containers
|
||||
this.cameraContainer = new PIXI.Container();
|
||||
this.videoContainer = new PIXI.Container();
|
||||
this.cameraContainer = new Container();
|
||||
this.videoContainer = new Container();
|
||||
this.app.stage.addChild(this.cameraContainer);
|
||||
this.cameraContainer.addChild(this.videoContainer);
|
||||
|
||||
@@ -91,7 +91,7 @@ export class FrameRenderer {
|
||||
await this.setupBackground();
|
||||
|
||||
// Setup blur filter for video container
|
||||
this.blurFilter = new PIXI.BlurFilter();
|
||||
this.blurFilter = new BlurFilter();
|
||||
this.blurFilter.quality = 3;
|
||||
this.blurFilter.resolution = this.app.renderer.resolution;
|
||||
this.blurFilter.blur = 0;
|
||||
@@ -120,7 +120,7 @@ export class FrameRenderer {
|
||||
}
|
||||
|
||||
// Setup mask
|
||||
this.maskGraphics = new PIXI.Graphics();
|
||||
this.maskGraphics = new Graphics();
|
||||
this.videoContainer.addChild(this.maskGraphics);
|
||||
this.videoContainer.mask = this.maskGraphics;
|
||||
}
|
||||
@@ -251,13 +251,13 @@ export class FrameRenderer {
|
||||
|
||||
// Create or update video sprite from VideoFrame
|
||||
if (!this.videoSprite) {
|
||||
const texture = PIXI.Texture.from(videoFrame as any);
|
||||
this.videoSprite = new PIXI.Sprite(texture);
|
||||
const texture = Texture.from(videoFrame as any);
|
||||
this.videoSprite = new Sprite(texture);
|
||||
this.videoContainer.addChild(this.videoSprite);
|
||||
} else {
|
||||
// Destroy old texture to avoid memory leaks, then create new one
|
||||
const oldTexture = this.videoSprite.texture;
|
||||
const newTexture = PIXI.Texture.from(videoFrame as any);
|
||||
const newTexture = Texture.from(videoFrame as any);
|
||||
this.videoSprite.texture = newTexture;
|
||||
oldTexture.destroy(true);
|
||||
}
|
||||
|
||||
@@ -39,4 +39,25 @@ export default defineConfig({
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
pure_funcs: ['console.log', 'console.debug']
|
||||
}
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'pixi': ['pixi.js'],
|
||||
'react-vendor': ['react', 'react-dom'],
|
||||
'video-processing': ['mediabunny', 'mp4box', '@fix-webm-duration/fix']
|
||||
}
|
||||
}
|
||||
},
|
||||
chunkSizeWarningLimit: 1000
|
||||
}
|
||||
})
|
||||
|
||||