feat: add generated local videos and portable build setup
Build and deploy Docker / deploy (push) Failing after 7m7s

This commit is contained in:
2026-10-08 14:18:38 +08:00
parent 36d8215f0b
commit c9606a6d0f
11 changed files with 110 additions and 162 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
import type { NextConfig } from 'next';
const config: NextConfig = { output: 'standalone', poweredByHeader: false, reactStrictMode: true };
const config: NextConfig = { output: process.platform === 'win32' ? undefined : 'standalone', poweredByHeader: false, reactStrictMode: true };
export default config;
+25 -5
View File
@@ -1,7 +1,27 @@
{
"name": "tiktok-ui", "version": "1.0.0", "private": true,
"scripts": {"dev":"next dev --hostname 0.0.0.0", "build":"next build", "start":"next start --hostname 0.0.0.0", "typecheck":"tsc --noEmit", "generate:videos":"node scripts/generate-videos.mjs"},
"dependencies": {"next":"^15.5.0", "react":"^19.1.0", "react-dom":"^19.1.0", "lucide-react":"^0.468.0"},
"devDependencies": {"typescript":"^5.9.0", "@types/node":"^22.0.0", "@types/react":"^19.0.0", "@types/react-dom":"^19.0.0", "@napi-rs/canvas":"^0.1.80", "ffmpeg-static":"^5.2.0"},
"packageManager":"pnpm@10.28.2"
"name": "tiktok-ui",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev --hostname 0.0.0.0",
"build": "next build",
"start": "next start --hostname 0.0.0.0",
"typecheck": "tsc --noEmit",
"generate:videos": "node scripts/generate-videos.mjs"
},
"dependencies": {
"lucide-react": "^0.468.0",
"next": "^15.5.0",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@napi-rs/canvas": "^0.1.80",
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.9.0"
},
"packageManager": "pnpm@10.28.2"
}
+81 -153
View File
@@ -21,6 +21,9 @@ importers:
specifier: ^19.1.0
version: 19.3.0(react@19.3.0)
devDependencies:
'@ffmpeg-installer/ffmpeg':
specifier: ^1.1.0
version: 1.1.0
'@napi-rs/canvas':
specifier: ^0.1.80
version: 0.1.100
@@ -33,22 +36,58 @@ importers:
'@types/react-dom':
specifier: ^19.0.0
version: 19.3.0(@types/react@19.3.0)
ffmpeg-static:
specifier: ^5.2.0
version: 5.3.0
typescript:
specifier: ^5.9.0
version: 5.9.3
packages:
'@derhuerst/http-basic@8.2.4':
resolution: {integrity: sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==}
engines: {node: '>=6.0.0'}
'@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
'@ffmpeg-installer/darwin-arm64@4.1.5':
resolution: {integrity: sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==}
cpu: [arm64]
os: [darwin]
'@ffmpeg-installer/darwin-x64@4.1.0':
resolution: {integrity: sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==}
cpu: [x64]
os: [darwin]
'@ffmpeg-installer/ffmpeg@1.1.0':
resolution: {integrity: sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==}
'@ffmpeg-installer/linux-arm64@4.1.4':
resolution: {integrity: sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==}
cpu: [arm64]
os: [linux]
'@ffmpeg-installer/linux-arm@4.1.3':
resolution: {integrity: sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==}
cpu: [arm]
os: [linux]
'@ffmpeg-installer/linux-ia32@4.1.0':
resolution: {integrity: sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==}
cpu: [ia32]
os: [linux]
'@ffmpeg-installer/linux-x64@4.1.0':
resolution: {integrity: sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==}
cpu: [x64]
os: [linux]
'@ffmpeg-installer/win32-ia32@4.1.0':
resolution: {integrity: sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==}
cpu: [ia32]
os: [win32]
'@ffmpeg-installer/win32-x64@4.1.0':
resolution: {integrity: sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==}
cpu: [x64]
os: [win32]
'@img/colour@1.1.0':
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
@@ -344,9 +383,6 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@types/node@10.17.60':
resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==}
'@types/node@22.20.5':
resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==}
@@ -358,68 +394,24 @@ packages:
'@types/react@19.3.0':
resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==}
agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
caniuse-lite@1.0.30001815:
resolution: {integrity: sha512-uDybUEyfgbyJJ93J1vikTylBX31AkdVYeVPeffMQpf8XN7hSfr0oZOn4VFOIMNvtU5+rHvC5Udg2TgtJKHm23Q==}
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
concat-stream@2.0.0:
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
engines: {'0': node >= 6.0}
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
ffmpeg-static@5.3.0:
resolution: {integrity: sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==}
engines: {node: '>=16'}
http-response-object@3.0.2:
resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==}
https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
lucide-react@0.468.0:
resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.20:
resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -446,9 +438,6 @@ packages:
sass:
optional: true
parse-cache-control@1.0.1:
resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -456,10 +445,6 @@ packages:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
react-dom@19.3.0:
resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==}
peerDependencies:
@@ -469,13 +454,6 @@ packages:
resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==}
engines: {node: '>=0.10.0'}
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
scheduler@0.28.0:
resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==}
@@ -497,9 +475,6 @@ packages:
resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
engines: {node: '>=0.10.0'}
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
styled-jsx@5.1.6:
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
engines: {node: '>= 12.0.0'}
@@ -516,9 +491,6 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
@@ -527,23 +499,48 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
snapshots:
'@derhuerst/http-basic@8.2.4':
dependencies:
caseless: 0.12.0
concat-stream: 2.0.0
http-response-object: 3.0.2
parse-cache-control: 1.0.1
'@emnapi/runtime@1.11.3':
dependencies:
tslib: 2.8.1
optional: true
'@ffmpeg-installer/darwin-arm64@4.1.5':
optional: true
'@ffmpeg-installer/darwin-x64@4.1.0':
optional: true
'@ffmpeg-installer/ffmpeg@1.1.0':
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
'@ffmpeg-installer/linux-arm64@4.1.4':
optional: true
'@ffmpeg-installer/linux-arm@4.1.3':
optional: true
'@ffmpeg-installer/linux-ia32@4.1.0':
optional: true
'@ffmpeg-installer/linux-x64@4.1.0':
optional: true
'@ffmpeg-installer/win32-ia32@4.1.0':
optional: true
'@ffmpeg-installer/win32-x64@4.1.0':
optional: true
'@img/colour@1.1.0':
optional: true
@@ -728,8 +725,6 @@ snapshots:
dependencies:
tslib: 2.8.1
'@types/node@10.17.60': {}
'@types/node@22.20.5':
dependencies:
undici-types: 6.21.0
@@ -742,66 +737,19 @@ snapshots:
dependencies:
csstype: 3.2.3
agent-base@6.0.2:
dependencies:
debug: 4.4.3
transitivePeerDependencies:
- supports-color
buffer-from@1.1.2: {}
caniuse-lite@1.0.30001815: {}
caseless@0.12.0: {}
client-only@0.0.1: {}
concat-stream@2.0.0:
dependencies:
buffer-from: 1.1.2
inherits: 2.0.4
readable-stream: 3.6.2
typedarray: 0.0.6
csstype@3.2.3: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
detect-libc@2.1.2:
optional: true
env-paths@2.2.1: {}
ffmpeg-static@5.3.0:
dependencies:
'@derhuerst/http-basic': 8.2.4
env-paths: 2.2.1
https-proxy-agent: 5.0.1
progress: 2.0.3
transitivePeerDependencies:
- supports-color
http-response-object@3.0.2:
dependencies:
'@types/node': 10.17.60
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.4.3
transitivePeerDependencies:
- supports-color
inherits@2.0.4: {}
lucide-react@0.468.0(react@19.3.0):
dependencies:
react: 19.3.0
ms@2.1.3: {}
nanoid@3.3.20: {}
next@15.5.27(@types/node@22.20.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0):
@@ -828,8 +776,6 @@ snapshots:
- '@types/node'
- babel-plugin-macros
parse-cache-control@1.0.1: {}
picocolors@1.1.1: {}
postcss@8.4.31:
@@ -838,8 +784,6 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.2
progress@2.0.3: {}
react-dom@19.3.0(react@19.3.0):
dependencies:
react: 19.3.0
@@ -847,14 +791,6 @@ snapshots:
react@19.3.0: {}
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
safe-buffer@5.2.1: {}
scheduler@0.28.0: {}
semver@7.8.5:
@@ -896,10 +832,6 @@ snapshots:
source-map-js@1.2.2: {}
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
styled-jsx@5.1.6(react@19.3.0):
dependencies:
client-only: 0.0.1
@@ -907,10 +839,6 @@ snapshots:
tslib@2.8.1: {}
typedarray@0.0.6: {}
typescript@5.9.3: {}
undici-types@6.21.0: {}
util-deprecate@1.0.2: {}
+1 -2
View File
@@ -1,2 +1 @@
onlyBuiltDependencies:
- ffmpeg-static
onlyBuiltDependencies: []
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.
+2 -1
View File
@@ -1,8 +1,9 @@
import { createCanvas } from '@napi-rs/canvas';
import ffmpeg from 'ffmpeg-static';
import installer from '@ffmpeg-installer/ffmpeg';
import { spawn } from 'node:child_process';
import { mkdir, writeFile } from 'node:fs/promises';
import { once } from 'node:events';
const ffmpeg=installer.path;
const W=960,H=960,FPS=24,SECONDS=8;
await mkdir('public/videos',{recursive:true});
const canvas=createCanvas(W,H), c=canvas.getContext('2d');