13
pkgs/api/Dockerfile
Normal file
13
pkgs/api/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm i -g pnpm && pnpm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pnpm build
|
||||
|
||||
CMD [ "node", "dist/index.js" ]
|
||||
10
pkgs/api/docker-compose.yaml
Normal file
10
pkgs/api/docker-compose.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "2598:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
24
pkgs/api/package.json
Normal file
24
pkgs/api/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Lexedia",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fastify": "^4.28.1",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"fluent-ffmpeg": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fluent-ffmpeg": "^2.1.26",
|
||||
"@types/node": "^22.5.4",
|
||||
"pino-pretty": "^11.2.2",
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
}
|
||||
741
pkgs/api/pnpm-lock.yaml
generated
Normal file
741
pkgs/api/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,741 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
fastify:
|
||||
specifier: ^4.28.1
|
||||
version: 4.28.1
|
||||
ffmpeg-static:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0
|
||||
fluent-ffmpeg:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
devDependencies:
|
||||
'@types/fluent-ffmpeg':
|
||||
specifier: ^2.1.26
|
||||
version: 2.1.26
|
||||
'@types/node':
|
||||
specifier: ^22.5.4
|
||||
version: 22.5.4
|
||||
pino-pretty:
|
||||
specifier: ^11.2.2
|
||||
version: 11.2.2
|
||||
typescript:
|
||||
specifier: ^5.6.2
|
||||
version: 5.6.2
|
||||
|
||||
packages:
|
||||
|
||||
'@derhuerst/http-basic@8.2.4':
|
||||
resolution: {integrity: sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@fastify/ajv-compiler@3.6.0':
|
||||
resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==}
|
||||
|
||||
'@fastify/error@3.4.1':
|
||||
resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==}
|
||||
|
||||
'@fastify/fast-json-stringify-compiler@4.3.0':
|
||||
resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==}
|
||||
|
||||
'@fastify/merge-json-schemas@0.1.1':
|
||||
resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==}
|
||||
|
||||
'@types/fluent-ffmpeg@2.1.26':
|
||||
resolution: {integrity: sha512-0JVF3wdQG+pN0ImwWD0bNgJiKF2OHg/7CDBHw5UIbRTvlnkgGHK6V5doE54ltvhud4o31/dEiHm23CAlxFiUQg==}
|
||||
|
||||
'@types/node@10.17.60':
|
||||
resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==}
|
||||
|
||||
'@types/node@22.5.4':
|
||||
resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
|
||||
abstract-logging@2.0.1:
|
||||
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
||||
|
||||
agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
ajv-formats@2.1.1:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
|
||||
ajv-formats@3.0.1:
|
||||
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
|
||||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
async@0.2.10:
|
||||
resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==}
|
||||
|
||||
atomic-sleep@1.0.0:
|
||||
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
avvio@8.4.0:
|
||||
resolution: {integrity: sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
caseless@0.12.0:
|
||||
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
concat-stream@2.0.0:
|
||||
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||
engines: {'0': node >= 6.0}
|
||||
|
||||
cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
dateformat@4.6.3:
|
||||
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
|
||||
|
||||
debug@4.3.7:
|
||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
end-of-stream@1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
|
||||
env-paths@2.2.1:
|
||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
|
||||
fast-content-type-parse@1.1.0:
|
||||
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
|
||||
|
||||
fast-copy@3.0.2:
|
||||
resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==}
|
||||
|
||||
fast-decode-uri-component@1.0.1:
|
||||
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
fast-json-stringify@5.16.1:
|
||||
resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==}
|
||||
|
||||
fast-querystring@1.1.2:
|
||||
resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
|
||||
|
||||
fast-redact@3.5.0:
|
||||
resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
fast-safe-stringify@2.1.1:
|
||||
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||
|
||||
fast-uri@2.4.0:
|
||||
resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==}
|
||||
|
||||
fast-uri@3.0.1:
|
||||
resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==}
|
||||
|
||||
fastify@4.28.1:
|
||||
resolution: {integrity: sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==}
|
||||
|
||||
fastq@1.17.1:
|
||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||
|
||||
ffmpeg-static@5.2.0:
|
||||
resolution: {integrity: sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
find-my-way@8.2.0:
|
||||
resolution: {integrity: sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
fluent-ffmpeg@2.1.3:
|
||||
resolution: {integrity: sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
help-me@5.0.0:
|
||||
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
||||
|
||||
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'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
joycon@3.1.1:
|
||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
json-schema-ref-resolver@1.0.1:
|
||||
resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==}
|
||||
|
||||
json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
|
||||
light-my-request@5.13.0:
|
||||
resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==}
|
||||
|
||||
minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
on-exit-leak-free@2.1.2:
|
||||
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
parse-cache-control@1.0.1:
|
||||
resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==}
|
||||
|
||||
pino-abstract-transport@1.2.0:
|
||||
resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==}
|
||||
|
||||
pino-pretty@11.2.2:
|
||||
resolution: {integrity: sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A==}
|
||||
hasBin: true
|
||||
|
||||
pino-std-serializers@7.0.0:
|
||||
resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
|
||||
|
||||
pino@9.4.0:
|
||||
resolution: {integrity: sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==}
|
||||
hasBin: true
|
||||
|
||||
process-warning@3.0.0:
|
||||
resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
|
||||
|
||||
process-warning@4.0.0:
|
||||
resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==}
|
||||
|
||||
process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
progress@2.0.3:
|
||||
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
pump@3.0.2:
|
||||
resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
|
||||
|
||||
quick-format-unescaped@4.0.4:
|
||||
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
|
||||
|
||||
readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
readable-stream@4.5.2:
|
||||
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
real-require@0.2.0:
|
||||
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ret@0.4.3:
|
||||
resolution: {integrity: sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
reusify@1.0.4:
|
||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
safe-regex2@3.1.0:
|
||||
resolution: {integrity: sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==}
|
||||
|
||||
safe-stable-stringify@2.5.0:
|
||||
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
secure-json-parse@2.7.0:
|
||||
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
||||
|
||||
semver@7.6.3:
|
||||
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
set-cookie-parser@2.7.0:
|
||||
resolution: {integrity: sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==}
|
||||
|
||||
sonic-boom@4.1.0:
|
||||
resolution: {integrity: sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==}
|
||||
|
||||
split2@4.2.0:
|
||||
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||
engines: {node: '>= 10.x'}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
thread-stream@3.1.0:
|
||||
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
|
||||
|
||||
toad-cache@3.7.0:
|
||||
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
typedarray@0.0.6:
|
||||
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
|
||||
|
||||
typescript@5.6.2:
|
||||
resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@6.19.8:
|
||||
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
which@1.3.1:
|
||||
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
||||
hasBin: true
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
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
|
||||
|
||||
'@fastify/ajv-compiler@3.6.0':
|
||||
dependencies:
|
||||
ajv: 8.17.1
|
||||
ajv-formats: 2.1.1(ajv@8.17.1)
|
||||
fast-uri: 2.4.0
|
||||
|
||||
'@fastify/error@3.4.1': {}
|
||||
|
||||
'@fastify/fast-json-stringify-compiler@4.3.0':
|
||||
dependencies:
|
||||
fast-json-stringify: 5.16.1
|
||||
|
||||
'@fastify/merge-json-schemas@0.1.1':
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
|
||||
'@types/fluent-ffmpeg@2.1.26':
|
||||
dependencies:
|
||||
'@types/node': 22.5.4
|
||||
|
||||
'@types/node@10.17.60': {}
|
||||
|
||||
'@types/node@22.5.4':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
abort-controller@3.0.0:
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
|
||||
abstract-logging@2.0.1: {}
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ajv-formats@2.1.1(ajv@8.17.1):
|
||||
optionalDependencies:
|
||||
ajv: 8.17.1
|
||||
|
||||
ajv-formats@3.0.1(ajv@8.17.1):
|
||||
optionalDependencies:
|
||||
ajv: 8.17.1
|
||||
|
||||
ajv@8.17.1:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-uri: 3.0.1
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
async@0.2.10: {}
|
||||
|
||||
atomic-sleep@1.0.0: {}
|
||||
|
||||
avvio@8.4.0:
|
||||
dependencies:
|
||||
'@fastify/error': 3.4.1
|
||||
fastq: 1.17.1
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
buffer@6.0.3:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
caseless@0.12.0: {}
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
concat-stream@2.0.0:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
typedarray: 0.0.6
|
||||
|
||||
cookie@0.6.0: {}
|
||||
|
||||
dateformat@4.6.3: {}
|
||||
|
||||
debug@4.3.7:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
end-of-stream@1.4.4:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
env-paths@2.2.1: {}
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
|
||||
events@3.3.0: {}
|
||||
|
||||
fast-content-type-parse@1.1.0: {}
|
||||
|
||||
fast-copy@3.0.2: {}
|
||||
|
||||
fast-decode-uri-component@1.0.1: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-json-stringify@5.16.1:
|
||||
dependencies:
|
||||
'@fastify/merge-json-schemas': 0.1.1
|
||||
ajv: 8.17.1
|
||||
ajv-formats: 3.0.1(ajv@8.17.1)
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-uri: 2.4.0
|
||||
json-schema-ref-resolver: 1.0.1
|
||||
rfdc: 1.4.1
|
||||
|
||||
fast-querystring@1.1.2:
|
||||
dependencies:
|
||||
fast-decode-uri-component: 1.0.1
|
||||
|
||||
fast-redact@3.5.0: {}
|
||||
|
||||
fast-safe-stringify@2.1.1: {}
|
||||
|
||||
fast-uri@2.4.0: {}
|
||||
|
||||
fast-uri@3.0.1: {}
|
||||
|
||||
fastify@4.28.1:
|
||||
dependencies:
|
||||
'@fastify/ajv-compiler': 3.6.0
|
||||
'@fastify/error': 3.4.1
|
||||
'@fastify/fast-json-stringify-compiler': 4.3.0
|
||||
abstract-logging: 2.0.1
|
||||
avvio: 8.4.0
|
||||
fast-content-type-parse: 1.1.0
|
||||
fast-json-stringify: 5.16.1
|
||||
find-my-way: 8.2.0
|
||||
light-my-request: 5.13.0
|
||||
pino: 9.4.0
|
||||
process-warning: 3.0.0
|
||||
proxy-addr: 2.0.7
|
||||
rfdc: 1.4.1
|
||||
secure-json-parse: 2.7.0
|
||||
semver: 7.6.3
|
||||
toad-cache: 3.7.0
|
||||
|
||||
fastq@1.17.1:
|
||||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
ffmpeg-static@5.2.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
|
||||
|
||||
find-my-way@8.2.0:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-querystring: 1.1.2
|
||||
safe-regex2: 3.1.0
|
||||
|
||||
fluent-ffmpeg@2.1.3:
|
||||
dependencies:
|
||||
async: 0.2.10
|
||||
which: 1.3.1
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
help-me@5.0.0: {}
|
||||
|
||||
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.3.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
ipaddr.js@1.9.1: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
json-schema-ref-resolver@1.0.1:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
||||
light-my-request@5.13.0:
|
||||
dependencies:
|
||||
cookie: 0.6.0
|
||||
process-warning: 3.0.0
|
||||
set-cookie-parser: 2.7.0
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
on-exit-leak-free@2.1.2: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
parse-cache-control@1.0.1: {}
|
||||
|
||||
pino-abstract-transport@1.2.0:
|
||||
dependencies:
|
||||
readable-stream: 4.5.2
|
||||
split2: 4.2.0
|
||||
|
||||
pino-pretty@11.2.2:
|
||||
dependencies:
|
||||
colorette: 2.0.20
|
||||
dateformat: 4.6.3
|
||||
fast-copy: 3.0.2
|
||||
fast-safe-stringify: 2.1.1
|
||||
help-me: 5.0.0
|
||||
joycon: 3.1.1
|
||||
minimist: 1.2.8
|
||||
on-exit-leak-free: 2.1.2
|
||||
pino-abstract-transport: 1.2.0
|
||||
pump: 3.0.2
|
||||
readable-stream: 4.5.2
|
||||
secure-json-parse: 2.7.0
|
||||
sonic-boom: 4.1.0
|
||||
strip-json-comments: 3.1.1
|
||||
|
||||
pino-std-serializers@7.0.0: {}
|
||||
|
||||
pino@9.4.0:
|
||||
dependencies:
|
||||
atomic-sleep: 1.0.0
|
||||
fast-redact: 3.5.0
|
||||
on-exit-leak-free: 2.1.2
|
||||
pino-abstract-transport: 1.2.0
|
||||
pino-std-serializers: 7.0.0
|
||||
process-warning: 4.0.0
|
||||
quick-format-unescaped: 4.0.4
|
||||
real-require: 0.2.0
|
||||
safe-stable-stringify: 2.5.0
|
||||
sonic-boom: 4.1.0
|
||||
thread-stream: 3.1.0
|
||||
|
||||
process-warning@3.0.0: {}
|
||||
|
||||
process-warning@4.0.0: {}
|
||||
|
||||
process@0.11.10: {}
|
||||
|
||||
progress@2.0.3: {}
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
dependencies:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
pump@3.0.2:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.4
|
||||
once: 1.4.0
|
||||
|
||||
quick-format-unescaped@4.0.4: {}
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readable-stream@4.5.2:
|
||||
dependencies:
|
||||
abort-controller: 3.0.0
|
||||
buffer: 6.0.3
|
||||
events: 3.3.0
|
||||
process: 0.11.10
|
||||
string_decoder: 1.3.0
|
||||
|
||||
real-require@0.2.0: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
ret@0.4.3: {}
|
||||
|
||||
reusify@1.0.4: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safe-regex2@3.1.0:
|
||||
dependencies:
|
||||
ret: 0.4.3
|
||||
|
||||
safe-stable-stringify@2.5.0: {}
|
||||
|
||||
secure-json-parse@2.7.0: {}
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
set-cookie-parser@2.7.0: {}
|
||||
|
||||
sonic-boom@4.1.0:
|
||||
dependencies:
|
||||
atomic-sleep: 1.0.0
|
||||
|
||||
split2@4.2.0: {}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
thread-stream@3.1.0:
|
||||
dependencies:
|
||||
real-require: 0.2.0
|
||||
|
||||
toad-cache@3.7.0: {}
|
||||
|
||||
typedarray@0.0.6: {}
|
||||
|
||||
typescript@5.6.2: {}
|
||||
|
||||
undici-types@6.19.8: {}
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
which@1.3.1:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
69
pkgs/api/src/index.ts
Normal file
69
pkgs/api/src/index.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import fastify from "fastify";
|
||||
import { tsToMpeg4 } from "./utils";
|
||||
|
||||
const envToLogger = {
|
||||
development: {
|
||||
transport: {
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
translateTime: "HH:MM:ss Z",
|
||||
ignore: "pid,hostname",
|
||||
},
|
||||
},
|
||||
},
|
||||
production: true,
|
||||
test: false,
|
||||
};
|
||||
|
||||
type Env = keyof typeof envToLogger;
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
readonly NODE_ENV: Env;
|
||||
readonly PORT: string | undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { NODE_ENV: env = "development" } = process.env;
|
||||
|
||||
const app = fastify({ logger: envToLogger[env] });
|
||||
|
||||
app.get("/", async (_, res) => res.redirect("https://bskyx.app"));
|
||||
|
||||
app.get<{ Params: { "*": string } }>(
|
||||
"/generate/*",
|
||||
{
|
||||
schema: {
|
||||
params: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"*": { type: "string" },
|
||||
},
|
||||
required: ["*"],
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req, res) => {
|
||||
let url = req.params["*"];
|
||||
|
||||
if (url.endsWith(".mp4")) {
|
||||
url = url.slice(0, -4);
|
||||
}
|
||||
|
||||
url = decodeURIComponent(url);
|
||||
|
||||
const result = await fetch(url).then((res) => res.arrayBuffer());
|
||||
|
||||
const video = await tsToMpeg4(Buffer.from(result));
|
||||
|
||||
res.header("Content-Type", "video/mp4");
|
||||
res.header("Cache-Control", "public, max-age=604800");
|
||||
res.send(video);
|
||||
}
|
||||
);
|
||||
|
||||
const port = process.env.PORT ? Number(process.env.PORT) : 3000;
|
||||
|
||||
app.listen({ port, host: "0.0.0.0" });
|
||||
50
pkgs/api/src/utils.ts
Normal file
50
pkgs/api/src/utils.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import ffmpeg from "fluent-ffmpeg";
|
||||
import ffmpegPath from "ffmpeg-static";
|
||||
|
||||
import { PassThrough } from "node:stream";
|
||||
import fs from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
ffmpeg.setFfmpegPath(ffmpegPath as string);
|
||||
|
||||
export function tsToMpeg4(buffer: Buffer | Uint8Array): Promise<Buffer> {
|
||||
return new Promise((res, rej) => {
|
||||
const input = new PassThrough();
|
||||
|
||||
input.end(buffer);
|
||||
|
||||
const tempFilePath = path.join(tmpdir(), `output-${Date.now()}.mp4`);
|
||||
|
||||
ffmpeg(input)
|
||||
.outputOption(
|
||||
"-c",
|
||||
"copy",
|
||||
"-movflags",
|
||||
"faststart",
|
||||
"-preset",
|
||||
"ultrafast"
|
||||
)
|
||||
.on("end", async () => {
|
||||
try {
|
||||
const ob = await fs.readFile(tempFilePath);
|
||||
await fs.unlink(tempFilePath);
|
||||
res(ob);
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
})
|
||||
.on("error", async (err, stdout, stderr) => {
|
||||
console.error("Error:", err.message);
|
||||
console.error("ffmpeg stdout:", stdout);
|
||||
console.error("ffmpeg stderr:", stderr);
|
||||
try {
|
||||
await fs.unlink(tempFilePath);
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
rej(err);
|
||||
})
|
||||
.save(tempFilePath);
|
||||
});
|
||||
}
|
||||
13
pkgs/api/tsconfig.json
Normal file
13
pkgs/api/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "./dist",
|
||||
},
|
||||
"include": ["./src/**/*.ts"],
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Wrangler",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 4365,
|
||||
"port": 8787,
|
||||
"cwd": "/",
|
||||
"resolveSourceMapLocations": null,
|
||||
"attachExistingChildren": false,
|
||||
0
pnpm-lock.yaml → pkgs/app/pnpm-lock.yaml
generated
0
pnpm-lock.yaml → pkgs/app/pnpm-lock.yaml
generated
@@ -4,11 +4,14 @@ import { Layout } from "./Layout";
|
||||
import { OEmbedTypes } from "../routes/getOEmbed";
|
||||
import { parseEmbedImages } from "../lib/parseEmbedImages";
|
||||
import { parseEmbedDescription } from "../lib/parseEmbedDescription";
|
||||
import { StreamInfo } from "../lib/processVideoEmbed";
|
||||
|
||||
interface PostProps {
|
||||
post: AppBskyFeedDefs.PostView;
|
||||
url: string;
|
||||
appDomain: string;
|
||||
videoMetadata?: StreamInfo[] | undefined;
|
||||
apiUrl: string;
|
||||
}
|
||||
|
||||
const Meta = ({ post }: { post: AppBskyFeedDefs.PostView }) => (
|
||||
@@ -17,6 +20,44 @@ const Meta = ({ post }: { post: AppBskyFeedDefs.PostView }) => (
|
||||
</>
|
||||
);
|
||||
|
||||
const Video = ({
|
||||
streamInfo,
|
||||
apiUrl,
|
||||
}: {
|
||||
streamInfo: StreamInfo;
|
||||
apiUrl: string;
|
||||
}) => {
|
||||
const url = `${apiUrl}generate/${encodeURIComponent(streamInfo.uri)}.mp4`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<meta property="twitter:card" content="player" />
|
||||
<meta property="twitter:player" content={url} />
|
||||
<meta property="twitter:player:stream" content={url} />
|
||||
<meta property="og:type" content="video.other" />
|
||||
<meta property="og:video" content={url} />
|
||||
<meta property="og:video:secure_url" content={url} />
|
||||
<meta property="og:video:type" content="video/mp4" />
|
||||
<meta
|
||||
property="og:video:width"
|
||||
content={streamInfo.resolution.width.toString()}
|
||||
/>
|
||||
<meta
|
||||
property="og:video:height"
|
||||
content={streamInfo.resolution.height.toString()}
|
||||
/>
|
||||
<meta
|
||||
property="twitter:player:width"
|
||||
content={streamInfo.resolution.width.toString()}
|
||||
/>
|
||||
<meta
|
||||
property="twitter:player:height"
|
||||
content={streamInfo.resolution.height.toString()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Images = ({
|
||||
images,
|
||||
}: {
|
||||
@@ -39,7 +80,13 @@ const Images = ({
|
||||
</>
|
||||
);
|
||||
|
||||
export const Post = ({ post, url, appDomain }: PostProps) => {
|
||||
export const Post = ({
|
||||
post,
|
||||
url,
|
||||
appDomain,
|
||||
videoMetadata,
|
||||
apiUrl,
|
||||
}: PostProps) => {
|
||||
const images = parseEmbedImages(post);
|
||||
const isAuthor = images === post.author.avatar;
|
||||
|
||||
@@ -61,7 +108,11 @@ export const Post = ({ post, url, appDomain }: PostProps) => {
|
||||
|
||||
{!isAuthor && <Meta post={post} />}
|
||||
|
||||
{images.length !== 0 && <Images images={images} />}
|
||||
{images.length !== 0 && !videoMetadata && <Images images={images} />}
|
||||
|
||||
{videoMetadata && (
|
||||
<Video apiUrl={apiUrl} streamInfo={videoMetadata.at(-1)!} />
|
||||
)}
|
||||
|
||||
<link
|
||||
rel="alternate"
|
||||
3
src/globals.d.ts → pkgs/app/src/globals.d.ts
vendored
3
src/globals.d.ts → pkgs/app/src/globals.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { BskyAgent } from "@atproto/api";
|
||||
import type {KVNamespace} from '@cloudflare/workers-types';
|
||||
import type { KVNamespace } from "@cloudflare/workers-types";
|
||||
|
||||
declare global {
|
||||
interface Env {
|
||||
@@ -8,6 +8,7 @@ declare global {
|
||||
BSKY_AUTH_USERNAME: string;
|
||||
BSKY_AUTH_PASSWORD: string;
|
||||
VIXBLUESKY_APP_DOMAIN: string;
|
||||
VIXBLUESKY_API_URL: string;
|
||||
bskyx: KVNamespace;
|
||||
};
|
||||
Variables: {
|
||||
106
pkgs/app/src/lib/processVideoEmbed.ts
Normal file
106
pkgs/app/src/lib/processVideoEmbed.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { AppBskyFeedDefs } from "@atproto/api";
|
||||
|
||||
export interface StreamInfo {
|
||||
bandwidth: number;
|
||||
resolution: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
codecs: string;
|
||||
uri: string;
|
||||
}
|
||||
|
||||
export interface M3U8Data {
|
||||
version: number;
|
||||
streams: StreamInfo[];
|
||||
}
|
||||
|
||||
export async function processVideoEmbed(post: AppBskyFeedDefs.PostView) {
|
||||
const videoUrl = post.embed?.playlist as string | undefined;
|
||||
|
||||
if (!videoUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = await fetch(videoUrl).then((res) => res.text());
|
||||
|
||||
const initalUrl = removeLastPathSegment(videoUrl);
|
||||
|
||||
const { streams } = await parseM3U8(initalUrl, contents);
|
||||
|
||||
return streams;
|
||||
}
|
||||
|
||||
async function parseM3U8(
|
||||
initalUrl: string,
|
||||
contents: string
|
||||
): Promise<M3U8Data> {
|
||||
const lines = contents
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l.length > 0);
|
||||
let version = 0;
|
||||
const streams: StreamInfo[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("#EXT-X-VERSION:")) {
|
||||
version = Number(line.split(":")[1]);
|
||||
} else if (line.startsWith("#EXT-X-STREAM-INF:")) {
|
||||
const attribs = line.split(":")[1].split(",");
|
||||
const sInfo: StreamInfo = {
|
||||
bandwidth: 0,
|
||||
resolution: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
codecs: "",
|
||||
uri: "",
|
||||
};
|
||||
|
||||
for (const attrib of attribs) {
|
||||
const [key, value] = attrib.split("=");
|
||||
switch (key) {
|
||||
case "BANDWIDTH":
|
||||
sInfo.bandwidth = Number(value);
|
||||
break;
|
||||
case "RESOLUTION":
|
||||
const [width, height] = value.split("x").map(Number);
|
||||
sInfo.resolution = { width, height };
|
||||
break;
|
||||
case "CODECS":
|
||||
sInfo.codecs = value.replaceAll('"', "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
streams.push(sInfo);
|
||||
} else if (line.includes("m3u8")) {
|
||||
const resolvedUrl = `${initalUrl}/${line}`;
|
||||
|
||||
const cont = await fetch(resolvedUrl).then((res) => res.text());
|
||||
|
||||
const parsed = await parseM3U8(removeLastPathSegment(resolvedUrl), cont);
|
||||
|
||||
streams.at(-1)!.uri = parsed.streams[0].uri;
|
||||
} else if (line.includes(".ts")) {
|
||||
streams.push({
|
||||
bandwidth: 0,
|
||||
resolution: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
codecs: "",
|
||||
uri: `${initalUrl}/${line}`,
|
||||
});
|
||||
} else {
|
||||
// Discard
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return { version, streams };
|
||||
}
|
||||
|
||||
function removeLastPathSegment(url: string) {
|
||||
return url.slice(0, url.lastIndexOf("/"));
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Handler } from "hono";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import { fetchPost } from "../lib/fetchPostData";
|
||||
import { Post } from "../components/Post";
|
||||
import { processVideoEmbed, StreamInfo } from "../lib/processVideoEmbed";
|
||||
|
||||
export const getPost: Handler<
|
||||
Env,
|
||||
@@ -16,11 +17,24 @@ export const getPost: Handler<
|
||||
});
|
||||
}
|
||||
|
||||
const fetchedPost = data.posts[0];
|
||||
|
||||
let videoMetaData: StreamInfo[] | undefined;
|
||||
|
||||
if (
|
||||
typeof fetchedPost.embed?.$type === "string" &&
|
||||
fetchedPost.embed?.$type.startsWith("app.bsky.embed.video")
|
||||
) {
|
||||
videoMetaData = await processVideoEmbed(fetchedPost);
|
||||
}
|
||||
|
||||
return c.html(
|
||||
<Post
|
||||
post={data.posts[0]}
|
||||
post={fetchedPost}
|
||||
url={c.req.path}
|
||||
appDomain={c.env.VIXBLUESKY_APP_DOMAIN}
|
||||
videoMetadata={videoMetaData}
|
||||
apiUrl={c.env.VIXBLUESKY_API_URL}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -8,6 +8,7 @@ route = { pattern = "bskyx.app/*", zone_name = "bskyx.app" }
|
||||
[vars]
|
||||
BSKY_SERVICE_URL="https://bsky.social/"
|
||||
VIXBLUESKY_APP_DOMAIN="bskyx.app"
|
||||
VIXBLUESKY_API_URL="https://api.bskyx.app/"
|
||||
|
||||
[[kv_namespaces]]
|
||||
binding = "bskyx"
|
||||
Reference in New Issue
Block a user