diff --git a/package-lock.json b/package-lock.json index 73fa37de..46f5b207 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,13 @@ "workspaces": [ "packages/*" ], + "dependencies": { + "uuid": "^9.0.1" + }, "devDependencies": { "@commitlint/cli": "^18.4.2", "@commitlint/config-conventional": "^18.4.2", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "eslint": "^8.54.0", @@ -3788,6 +3792,243 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ffmpeg-installer/darwin-arm64": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz", + "integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffmpeg-installer/darwin-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz", + "integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffmpeg-installer/ffmpeg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz", + "integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==", + "optionalDependencies": { + "@ffmpeg-installer/darwin-arm64": "4.1.5", + "@ffmpeg-installer/darwin-x64": "4.1.0", + "@ffmpeg-installer/linux-arm": "4.1.3", + "@ffmpeg-installer/linux-arm64": "4.1.4", + "@ffmpeg-installer/linux-ia32": "4.1.0", + "@ffmpeg-installer/linux-x64": "4.1.0", + "@ffmpeg-installer/win32-ia32": "4.1.0", + "@ffmpeg-installer/win32-x64": "4.1.0" + } + }, + "node_modules/@ffmpeg-installer/linux-arm": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz", + "integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==", + "cpu": [ + "arm" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz", + "integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz", + "integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==", + "cpu": [ + "ia32" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz", + "integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/win32-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz", + "integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ffmpeg-installer/win32-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz", + "integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ffprobe-installer/darwin-arm64": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-arm64/-/darwin-arm64-5.0.1.tgz", + "integrity": "sha512-vwNCNjokH8hfkbl6m95zICHwkSzhEvDC3GVBcUp5HX8+4wsX10SP3B+bGur7XUzTIZ4cQpgJmEIAx6TUwRepMg==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffprobe-installer/darwin-x64": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-x64/-/darwin-x64-5.1.0.tgz", + "integrity": "sha512-J+YGscZMpQclFg31O4cfVRGmDpkVsQ2fZujoUdMAAYcP0NtqpC49Hs3SWJpBdsGB4VeqOt5TTm1vSZQzs1NkhA==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffprobe-installer/ffprobe": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/ffprobe/-/ffprobe-2.1.2.tgz", + "integrity": "sha512-ZNvwk4f2magF42Zji2Ese16SMj9BS7Fui4kRjg6gTYTxY3gWZNpg85n4MIfQyI9nimHg4x/gT6FVkp/bBDuBwg==", + "engines": { + "node": ">=14.21.2" + }, + "optionalDependencies": { + "@ffprobe-installer/darwin-arm64": "5.0.1", + "@ffprobe-installer/darwin-x64": "5.1.0", + "@ffprobe-installer/linux-arm": "5.2.0", + "@ffprobe-installer/linux-arm64": "5.2.0", + "@ffprobe-installer/linux-ia32": "5.2.0", + "@ffprobe-installer/linux-x64": "5.2.0", + "@ffprobe-installer/win32-ia32": "5.1.0", + "@ffprobe-installer/win32-x64": "5.1.0" + } + }, + "node_modules/@ffprobe-installer/linux-arm": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm/-/linux-arm-5.2.0.tgz", + "integrity": "sha512-PF5HqEhCY7WTWHtLDYbA/+rLS+rhslWvyBlAG1Fk8VzVlnRdl93o6hy7DE2kJgxWQbFaR3ZktPQGEzfkrmQHvQ==", + "cpu": [ + "arm" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffprobe-installer/linux-arm64": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm64/-/linux-arm64-5.2.0.tgz", + "integrity": "sha512-X1VvWtlLs6ScP73biVLuHD5ohKJKsMTa0vafCESOen4mOoNeLAYbxOVxDWAdFz9cpZgRiloFj5QD6nDj8E28yQ==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffprobe-installer/linux-ia32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-ia32/-/linux-ia32-5.2.0.tgz", + "integrity": "sha512-TFVK5sasXyXhbIG7LtPRDmtkrkOsInwKcL43iEvEw+D9vCS2rc//mn9/0Q+BR0UoJEiMK4+ApYr/3LLVUBPOCQ==", + "cpu": [ + "ia32" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffprobe-installer/linux-x64": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-x64/-/linux-x64-5.2.0.tgz", + "integrity": "sha512-D3UeqTLYPNs7pBWPLUYGehPdRVqU8eACox4OZy3pZUZatxye2YKlvBwEfaLdL1v2Z4FOAlLUhms0kY8m8kqSRA==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffprobe-installer/win32-ia32": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-ia32/-/win32-ia32-5.1.0.tgz", + "integrity": "sha512-5O3vOoNRxmut0/Nu9vSazTdSHasrr+zPT2B3Hm7kjmO3QVFcIfVImS6ReQnZeSy8JPJOqXts5kX5x/3KOX54XQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ffprobe-installer/win32-x64": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-x64/-/win32-x64-5.1.0.tgz", + "integrity": "sha512-jMGYeAgkrdn4e2vvYt/qakgHRE3CPju4bn5TmdPfoAm1BlX1mY9cyMd8gf5vSzI8gH8Zq5WQAyAkmekX/8TSTg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -4709,19 +4950,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lerna/create/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@lerna/create/node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -5051,6 +5279,10 @@ "resolved": "packages/examples", "link": true }, + "node_modules/@motion-canvas/ffmpeg": { + "resolved": "packages/ffmpeg", + "link": true + }, "node_modules/@motion-canvas/internal": { "resolved": "packages/internal", "link": true @@ -7078,6 +7310,15 @@ "@types/range-parser": "*" } }, + "node_modules/@types/fluent-ffmpeg": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.24.tgz", + "integrity": "sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/follow-redirects": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.1.tgz", @@ -7308,6 +7549,12 @@ "version": "2.0.6", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.3", "license": "MIT", @@ -8292,8 +8539,7 @@ "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "node_modules/asynckit": { "version": "0.4.0", @@ -12607,6 +12853,29 @@ "devOptional": true, "license": "ISC" }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", + "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", + "dependencies": { + "async": ">=0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fluent-ffmpeg/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/flux": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", @@ -15941,19 +16210,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lerna/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/lerna/node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -17220,13 +17476,6 @@ "uuid": "^9.0.0" } }, - "node_modules/mermaid/node_modules/uuid": { - "version": "9.0.0", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/methods": { "version": "1.1.2", "license": "MIT", @@ -22711,6 +22960,14 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -24520,8 +24777,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -25855,6 +26117,22 @@ "@motion-canvas/vite-plugin": "*" } }, + "packages/ffmpeg": { + "name": "@motion-canvas/ffmpeg", + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@ffmpeg-installer/ffmpeg": "^1.1.0", + "@ffprobe-installer/ffprobe": "^2.0.0", + "@motion-canvas/core": "^3.14.1", + "@motion-canvas/vite-plugin": "^3.14.1", + "fluent-ffmpeg": "^2.1.2", + "vite": "4.x" + }, + "devDependencies": { + "@types/fluent-ffmpeg": "^2.1.21" + } + }, "packages/internal": { "name": "@motion-canvas/internal", "version": "0.0.0", @@ -25897,7 +26175,8 @@ "license": "MIT", "dependencies": { "@motion-canvas/2d": "*", - "@motion-canvas/core": "*" + "@motion-canvas/core": "*", + "@motion-canvas/ffmpeg": "*" }, "devDependencies": { "@motion-canvas/ui": "*", @@ -28540,6 +28819,132 @@ "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", "devOptional": true }, + "@ffmpeg-installer/darwin-arm64": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz", + "integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==", + "optional": true + }, + "@ffmpeg-installer/darwin-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz", + "integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==", + "optional": true + }, + "@ffmpeg-installer/ffmpeg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz", + "integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==", + "requires": { + "@ffmpeg-installer/darwin-arm64": "4.1.5", + "@ffmpeg-installer/darwin-x64": "4.1.0", + "@ffmpeg-installer/linux-arm": "4.1.3", + "@ffmpeg-installer/linux-arm64": "4.1.4", + "@ffmpeg-installer/linux-ia32": "4.1.0", + "@ffmpeg-installer/linux-x64": "4.1.0", + "@ffmpeg-installer/win32-ia32": "4.1.0", + "@ffmpeg-installer/win32-x64": "4.1.0" + } + }, + "@ffmpeg-installer/linux-arm": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz", + "integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==", + "optional": true + }, + "@ffmpeg-installer/linux-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz", + "integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==", + "optional": true + }, + "@ffmpeg-installer/linux-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz", + "integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==", + "optional": true + }, + "@ffmpeg-installer/linux-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz", + "integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==", + "optional": true + }, + "@ffmpeg-installer/win32-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz", + "integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==", + "optional": true + }, + "@ffmpeg-installer/win32-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz", + "integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==", + "optional": true + }, + "@ffprobe-installer/darwin-arm64": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-arm64/-/darwin-arm64-5.0.1.tgz", + "integrity": "sha512-vwNCNjokH8hfkbl6m95zICHwkSzhEvDC3GVBcUp5HX8+4wsX10SP3B+bGur7XUzTIZ4cQpgJmEIAx6TUwRepMg==", + "optional": true + }, + "@ffprobe-installer/darwin-x64": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-x64/-/darwin-x64-5.1.0.tgz", + "integrity": "sha512-J+YGscZMpQclFg31O4cfVRGmDpkVsQ2fZujoUdMAAYcP0NtqpC49Hs3SWJpBdsGB4VeqOt5TTm1vSZQzs1NkhA==", + "optional": true + }, + "@ffprobe-installer/ffprobe": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/ffprobe/-/ffprobe-2.1.2.tgz", + "integrity": "sha512-ZNvwk4f2magF42Zji2Ese16SMj9BS7Fui4kRjg6gTYTxY3gWZNpg85n4MIfQyI9nimHg4x/gT6FVkp/bBDuBwg==", + "requires": { + "@ffprobe-installer/darwin-arm64": "5.0.1", + "@ffprobe-installer/darwin-x64": "5.1.0", + "@ffprobe-installer/linux-arm": "5.2.0", + "@ffprobe-installer/linux-arm64": "5.2.0", + "@ffprobe-installer/linux-ia32": "5.2.0", + "@ffprobe-installer/linux-x64": "5.2.0", + "@ffprobe-installer/win32-ia32": "5.1.0", + "@ffprobe-installer/win32-x64": "5.1.0" + } + }, + "@ffprobe-installer/linux-arm": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm/-/linux-arm-5.2.0.tgz", + "integrity": "sha512-PF5HqEhCY7WTWHtLDYbA/+rLS+rhslWvyBlAG1Fk8VzVlnRdl93o6hy7DE2kJgxWQbFaR3ZktPQGEzfkrmQHvQ==", + "optional": true + }, + "@ffprobe-installer/linux-arm64": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm64/-/linux-arm64-5.2.0.tgz", + "integrity": "sha512-X1VvWtlLs6ScP73biVLuHD5ohKJKsMTa0vafCESOen4mOoNeLAYbxOVxDWAdFz9cpZgRiloFj5QD6nDj8E28yQ==", + "optional": true + }, + "@ffprobe-installer/linux-ia32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-ia32/-/linux-ia32-5.2.0.tgz", + "integrity": "sha512-TFVK5sasXyXhbIG7LtPRDmtkrkOsInwKcL43iEvEw+D9vCS2rc//mn9/0Q+BR0UoJEiMK4+ApYr/3LLVUBPOCQ==", + "optional": true + }, + "@ffprobe-installer/linux-x64": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-x64/-/linux-x64-5.2.0.tgz", + "integrity": "sha512-D3UeqTLYPNs7pBWPLUYGehPdRVqU8eACox4OZy3pZUZatxye2YKlvBwEfaLdL1v2Z4FOAlLUhms0kY8m8kqSRA==", + "optional": true + }, + "@ffprobe-installer/win32-ia32": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-ia32/-/win32-ia32-5.1.0.tgz", + "integrity": "sha512-5O3vOoNRxmut0/Nu9vSazTdSHasrr+zPT2B3Hm7kjmO3QVFcIfVImS6ReQnZeSy8JPJOqXts5kX5x/3KOX54XQ==", + "optional": true + }, + "@ffprobe-installer/win32-x64": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-x64/-/win32-x64-5.1.0.tgz", + "integrity": "sha512-jMGYeAgkrdn4e2vvYt/qakgHRE3CPju4bn5TmdPfoAm1BlX1mY9cyMd8gf5vSzI8gH8Zq5WQAyAkmekX/8TSTg==", + "optional": true + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -29218,12 +29623,6 @@ "glob": "^9.2.0" } }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true - }, "write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -29612,6 +30011,18 @@ "@motion-canvas/vite-plugin": "*" } }, + "@motion-canvas/ffmpeg": { + "version": "file:packages/ffmpeg", + "requires": { + "@ffmpeg-installer/ffmpeg": "^1.1.0", + "@ffprobe-installer/ffprobe": "^2.0.0", + "@motion-canvas/core": "^3.14.1", + "@motion-canvas/vite-plugin": "^3.14.1", + "@types/fluent-ffmpeg": "^2.1.21", + "fluent-ffmpeg": "^2.1.2", + "vite": "4.x" + } + }, "@motion-canvas/internal": { "version": "file:packages/internal", "requires": { @@ -29646,6 +30057,7 @@ "requires": { "@motion-canvas/2d": "*", "@motion-canvas/core": "*", + "@motion-canvas/ffmpeg": "*", "@motion-canvas/ui": "*", "@motion-canvas/vite-plugin": "*" } @@ -31067,6 +31479,15 @@ "@types/range-parser": "*" } }, + "@types/fluent-ffmpeg": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.24.tgz", + "integrity": "sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/follow-redirects": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.1.tgz", @@ -31270,6 +31691,12 @@ "@types/unist": { "version": "2.0.6" }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "@types/ws": { "version": "8.5.3", "requires": { @@ -31998,8 +32425,7 @@ "async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "asynckit": { "version": "0.4.0", @@ -34830,6 +35256,25 @@ "version": "3.2.6", "devOptional": true }, + "fluent-ffmpeg": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", + "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", + "requires": { + "async": ">=0.2.9", + "which": "^1.1.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "flux": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", @@ -37115,12 +37560,6 @@ "glob": "^9.2.0" } }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true - }, "write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -38014,11 +38453,6 @@ "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.1.2", "uuid": "^9.0.0" - }, - "dependencies": { - "uuid": { - "version": "9.0.0" - } } }, "methods": { @@ -41833,6 +42267,13 @@ "faye-websocket": "^0.11.3", "uuid": "^8.3.2", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "socks": { @@ -43056,7 +43497,9 @@ "version": "1.0.1" }, "uuid": { - "version": "8.3.2" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache": { "version": "2.3.0", diff --git a/package.json b/package.json index ab290307..6b620b26 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "devDependencies": { "@commitlint/cli": "^18.4.2", "@commitlint/config-conventional": "^18.4.2", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "eslint": "^8.54.0", @@ -53,5 +54,8 @@ "lint-staged": { "*.{ts,tsx}": "eslint --fix", "*.{js,jsx,ts,tsx,md,scss}": "prettier --write" + }, + "dependencies": { + "uuid": "^9.0.1" } } diff --git a/packages/2d/src/lib/components/Audio.ts b/packages/2d/src/lib/components/Audio.ts new file mode 100644 index 00000000..07577e26 --- /dev/null +++ b/packages/2d/src/lib/components/Audio.ts @@ -0,0 +1,130 @@ +import {DependencyContext, PlaybackState, viaProxy} from '@motion-canvas/core'; +import {computed, nodeName} from '../decorators'; +import {Media, MediaProps} from './Media'; + +@nodeName('Audio') +export class Audio extends Media { + private static readonly pool: Record = {}; + + public constructor(props: MediaProps) { + super(props); + } + + protected mediaElement(): HTMLAudioElement { + return this.audio(); + } + + protected seekedMedia(): HTMLAudioElement { + return this.seekedAudio(); + } + + protected fastSeekedMedia(): HTMLAudioElement { + return this.fastSeekedAudio(); + } + + @computed() + protected audio(): HTMLAudioElement { + const src = viaProxy(this.src()); + const key = `${this.key}/${src}`; + let audio = Audio.pool[key]; + if (!audio) { + audio = document.createElement('audio'); + audio.crossOrigin = 'anonymous'; + audio.src = src; + Audio.pool[key] = audio; + } + if (audio.readyState < 2) { + DependencyContext.collectPromise( + new Promise(resolve => { + const listener = () => { + resolve(); + audio.removeEventListener('canplay', listener); + }; + audio.addEventListener('canplay', listener); + }), + ); + } + + return audio; + } + + @computed() + protected seekedAudio(): HTMLAudioElement { + const audio = this.audio(); + + audio.addEventListener('ended', () => { + this.pause(); + }); + + if (!(this.time() < audio.duration)) { + this.pause(); + return audio; + } + + const time = this.clampTime(this.time()); + audio.playbackRate = this.playbackRate(); + + if (!audio.paused) { + audio.pause(); + } + + if (this.lastTime === time) { + return audio; + } + + this.setCurrentTime(time); + + return audio; + } + + @computed() + protected fastSeekedAudio(): HTMLAudioElement { + const audio = this.audio(); + + if (!(this.time() < audio.duration)) { + this.pause(); + return audio; + } + + const time = this.clampTime(this.time()); + + audio.playbackRate = this.playbackRate(); + + if (this.lastTime === time) { + return audio; + } + + const playing = + this.playing() && time < audio.duration && audio.playbackRate > 0; + if (playing) { + if (audio.paused) { + DependencyContext.collectPromise(audio.play()); + } + } else { + if (!audio.paused) { + audio.pause(); + } + } + if (Math.abs(audio.currentTime - time) > 0.3) { + this.setCurrentTime(time); + } else if (!playing) { + audio.currentTime = time; + } + + return audio; + } + + protected override draw(context: CanvasRenderingContext2D) { + const playbackState = this.view().playbackState(); + + playbackState === PlaybackState.Playing || + playbackState === PlaybackState.Presenting + ? this.fastSeekedAudio() + : this.seekedAudio(); + + context.save(); + context.restore(); + + this.drawChildren(context); + } +} diff --git a/packages/2d/src/lib/components/Media.ts b/packages/2d/src/lib/components/Media.ts new file mode 100644 index 00000000..f283bd8f --- /dev/null +++ b/packages/2d/src/lib/components/Media.ts @@ -0,0 +1,165 @@ +import { + DependencyContext, + SignalValue, + SimpleSignal, + clamp, + isReactive, + useLogger, + useThread, +} from '@motion-canvas/core'; +import {computed, initial, signal} from '../decorators'; +import {Rect, RectProps} from './Rect'; +import reactivePlaybackRate from './__logs__/reactive-playback-rate.md'; + +export interface MediaProps extends RectProps { + src?: SignalValue; + loop?: SignalValue; + playbackRate?: number; + time?: SignalValue; + play?: boolean; +} + +export abstract class Media extends Rect { + @signal() + public declare readonly src: SimpleSignal; + + @initial(false) + @signal() + public declare readonly loop: SimpleSignal; + + @initial(1) + @signal() + public declare readonly playbackRate: SimpleSignal; + + @initial(0) + @signal() + protected declare readonly time: SimpleSignal; + + @initial(false) + @signal() + protected declare readonly playing: SimpleSignal; + + protected lastTime = -1; + + public constructor(props: MediaProps) { + super(props); + if (props.play) { + this.play(); + } + } + + public isPlaying(): boolean { + return this.playing(); + } + + public getCurrentTime(): number { + return this.clampTime(this.time()); + } + + public getDuration(): number { + return this.mediaElement().duration; + } + + public override dispose() { + this.pause(); + this.remove(); + super.dispose(); + } + + @computed() + public override completion(): number { + return this.clampTime(this.time()) / this.getDuration(); + } + + protected abstract mediaElement(): HTMLMediaElement; + + protected abstract seekedMedia(): HTMLMediaElement; + + protected abstract fastSeekedMedia(): HTMLMediaElement; + + protected abstract override draw(context: CanvasRenderingContext2D): void; + + protected setCurrentTime(value: number) { + const media = this.mediaElement(); + if (media.readyState < 2) return; + + media.currentTime = value; + this.lastTime = value; + if (media.seeking) { + DependencyContext.collectPromise( + new Promise(resolve => { + const listener = () => { + resolve(); + media.removeEventListener('seeked', listener); + }; + media.addEventListener('seeked', listener); + }), + ); + } + } + + protected setPlaybackRate(playbackRate: number) { + let value: number; + if (isReactive(playbackRate)) { + value = playbackRate(); + useLogger().warn({ + message: 'Invalid value set as the playback rate', + remarks: reactivePlaybackRate, + inspect: this.key, + stack: new Error().stack, + }); + } else { + value = playbackRate; + } + this.playbackRate.context.setter(value); + + if (this.playing()) { + if (value === 0) { + this.pause(); + } else { + const time = useThread().time; + const start = time(); + const offset = this.time(); + this.time(() => this.clampTime(offset + (time() - start) * value)); + } + } + } + + public play() { + const time = useThread().time; + const start = time(); + const offset = this.time(); + const playbackRate = this.playbackRate(); + this.playing(true); + this.time(() => this.clampTime(offset + (time() - start) * playbackRate)); + } + + public pause() { + this.playing(false); + this.time.save(); + this.mediaElement().pause(); + } + + public seek(time: number) { + const playing = this.playing(); + this.time(this.clampTime(time)); + if (playing) { + this.play(); + } else { + this.pause(); + } + } + + public clampTime(time: number): number { + const duration = this.getDuration(); + if (this.loop()) { + time %= duration; + } + return clamp(0, duration, time); + } + + protected override collectAsyncResources() { + super.collectAsyncResources(); + this.seekedMedia(); + } +} diff --git a/packages/2d/src/lib/components/Video.ts b/packages/2d/src/lib/components/Video.ts index 32536d84..c169b6a0 100644 --- a/packages/2d/src/lib/components/Video.ts +++ b/packages/2d/src/lib/components/Video.ts @@ -5,22 +5,14 @@ import { SerializedVector2, SignalValue, SimpleSignal, - clamp, - isReactive, - useLogger, - useThread, + viaProxy, } from '@motion-canvas/core'; import {computed, initial, nodeName, signal} from '../decorators'; import {DesiredLength} from '../partials'; import {drawImage} from '../utils'; -import {Rect, RectProps} from './Rect'; -import reactivePlaybackRate from './__logs__/reactive-playback-rate.md'; +import {Media, MediaProps} from './Media'; -export interface VideoProps extends RectProps { - /** - * {@inheritDoc Video.src} - */ - src?: SignalValue; +export interface VideoProps extends MediaProps { /** * {@inheritDoc Video.alpha} */ @@ -29,43 +21,10 @@ export interface VideoProps extends RectProps { * {@inheritDoc Video.smoothing} */ smoothing?: SignalValue; - /** - * {@inheritDoc Video.loop} - */ - loop?: SignalValue; - /** - * {@inheritDoc Video.playbackRate} - */ - playbackRate?: number; - /** - * The starting time for this video in seconds. - */ - time?: SignalValue; - play?: boolean; } @nodeName('Video') -export class Video extends Rect { - private static readonly pool: Record = {}; - - /** - * The source of this video. - * - * @example - * Using a local video: - * ```tsx - * import video from './example.mp4'; - * // ... - * view.add(