diff --git a/electron-builder.json5 b/electron-builder.json5 index 49774ba..192026d 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -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" diff --git a/package-lock.json b/package-lock.json index c20e433..eaf8d56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 598b4d6..d1b319b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/public/wallpaper.png b/public/wallpaper.png deleted file mode 100644 index 0e51d83..0000000 Binary files a/public/wallpaper.png and /dev/null differ diff --git a/public/wallpapers/wallpaper1.jpg b/public/wallpapers/wallpaper1.jpg index d91b530..d8d9ba6 100644 Binary files a/public/wallpapers/wallpaper1.jpg and b/public/wallpapers/wallpaper1.jpg differ diff --git a/public/wallpapers/wallpaper4.jpg b/public/wallpapers/wallpaper4.jpg index 3620da9..49f2816 100644 Binary files a/public/wallpapers/wallpaper4.jpg and b/public/wallpapers/wallpaper4.jpg differ diff --git a/public/wallpapers/wallpaper5.jpg b/public/wallpapers/wallpaper5.jpg index dec36ce..294ca32 100644 Binary files a/public/wallpapers/wallpaper5.jpg and b/public/wallpapers/wallpaper5.jpg differ diff --git a/public/wallpapers/wallpaper6.jpg b/public/wallpapers/wallpaper6.jpg index 23314b2..419569d 100644 Binary files a/public/wallpapers/wallpaper6.jpg and b/public/wallpapers/wallpaper6.jpg differ diff --git a/public/wallpapers/wallpaper7.jpg b/public/wallpapers/wallpaper7.jpg index 6c23515..aa9d818 100644 Binary files a/public/wallpapers/wallpaper7.jpg and b/public/wallpapers/wallpaper7.jpg differ diff --git a/public/wallpapers/wallpaper8.jpg b/public/wallpapers/wallpaper8.jpg index 9cf1193..14e5ca5 100644 Binary files a/public/wallpapers/wallpaper8.jpg and b/public/wallpapers/wallpaper8.jpg differ diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 0a335e2..62c3d40 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -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; pause: () => void; } @@ -56,10 +56,10 @@ const VideoPlayback = forwardRef(({ }, ref) => { const videoRef = useRef(null); const containerRef = useRef(null); - const appRef = useRef(null); - const videoSpriteRef = useRef(null); - const videoContainerRef = useRef(null); - const cameraContainerRef = useRef(null); + const appRef = useRef(null); + const videoSpriteRef = useRef(null); + const videoContainerRef = useRef(null); + const cameraContainerRef = useRef(null); const timeUpdateAnimationRef = useRef(null); const [pixiReady, setPixiReady] = useState(false); const [videoReady, setVideoReady] = useState(false); @@ -69,7 +69,7 @@ const VideoPlayback = forwardRef(({ const zoomRegionsRef = useRef([]); const selectedZoomIdRef = useRef(null); const animationStateRef = useRef({ scale: 1, focusX: DEFAULT_FOCUS.cx, focusY: DEFAULT_FOCUS.cy }); - const blurFilterRef = useRef(null); + const blurFilterRef = useRef(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(({ 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(null); + const maskGraphicsRef = useRef(null); const isPlayingRef = useRef(isPlaying); const isSeekingRef = useRef(false); const allowPlaybackRef = useRef(false); @@ -398,10 +398,10 @@ const VideoPlayback = forwardRef(({ 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(({ 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(({ 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(({ focusY: DEFAULT_FOCUS.cy, }; - const blurFilter = new PIXI.BlurFilter(); + const blurFilter = new BlurFilter(); blurFilter.quality = 3; blurFilter.resolution = app.renderer.resolution; blurFilter.blur = 0; diff --git a/src/components/video-editor/videoPlayback/layoutUtils.ts b/src/components/video-editor/videoPlayback/layoutUtils.ts index a83c982..9a3d290 100644 --- a/src/components/video-editor/videoPlayback/layoutUtils.ts +++ b/src/components/video-editor/videoPlayback/layoutUtils.ts @@ -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; diff --git a/src/components/video-editor/videoPlayback/zoomTransform.ts b/src/components/video-editor/videoPlayback/zoomTransform.ts index a3f8646..145c785 100644 --- a/src/components/video-editor/videoPlayback/zoomTransform.ts +++ b/src/components/video-editor/videoPlayback/zoomTransform.ts @@ -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; diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index 0f947b4..020d1bc 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -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); } diff --git a/vite.config.ts b/vite.config.ts index 1bd814d..589d3f7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -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 + } })