createFunscript fixes
Some checks failed
ci / build (push) Failing after 0s
ci / Tests & Checks (push) Failing after 1s

This commit is contained in:
CJ_Clippy 2025-07-19 07:18:48 -08:00
parent 110565d536
commit fad0d49776
175 changed files with 1429 additions and 1404 deletions

View File

@ -1,15 +0,0 @@
# See https://caddyserver.com/docs/caddyfile for more details
{
admin 0.0.0.0:2020
auto_https disable_certs
http_port 8800
https_port 4443
}
:8082 {
root * {$CADDY_ROOT_DIR}
log {
output file {$CADDY_LOG_DIR}/caddy.log
}
file_server
}

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello World!</title>
</head>
<body>
Hello World!
</body>
</html>

View File

@ -1,46 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.12.0/.schema/devbox.schema.json",
"packages": [
"nodejs@20",
"ffmpeg@latest",
"yt-dlp@latest",
"lazydocker@latest",
"chisel@latest",
"entr@latest",
"act@latest",
"git-subrepo@latest",
"opentofu@latest",
"ggshield@latest",
"python310@latest",
"python310Packages.pip@latest",
"caddy@latest"
],
"env": {
"DEVBOX_COREPACK_ENABLED": "true",
"ENV": "development",
"KUBECONFIG": "$HOME/.kube/futureporn.yaml",
"VENV_DIR": ".venv"
},
"shell": {
"init_hook": [
"echo Welcome to Futureporn devbox",
". $VENV_DIR/bin/activate",
"pip install -r requirements.txt"
],
"scripts": {
"tunnel": "dotenvx run -f ./.env.development -- chisel client bright.fp.sbtp.xyz:9090 R:4000",
"backup": "docker exec -t postgres_db pg_dumpall -c -U postgres > ./backups/dev_`date +%Y-%m-%d_%H_%M_%S`.sql",
"act": "dotenvx run -f ./.env.testing -- act -W ./.gitea/workflows --secret-file .env.development",
"act:builder": "dotenvx run -f ./.env.testing -- act --env-file .env.testing -W ./.gitea/workflows/builder.yaml --secret-file .env.testing --var-file .env.testing --insecure-secrets",
"act:tests": "dotenvx run -f ./.env.testing -- act --env-file .env.testing -W ./.gitea/workflows/tests.yaml --secret-file .env.testing --var-file .env.testing --insecure-secrets",
"bright:compile:watch": "cd ./apps/bright && find . -type f -name \"*.ex\" -o -name \"*.exs\" | entr -r mix compile --warnings-as-errors",
"bright:compile:watch2": "cd ./apps/bright && pnpx chokidar-cli \"**/*\" -i \"deps/**\" -i \"_build/**\" -c \"mix compile --warnings-as-errors\"",
"bright:dev": "cd ./apps/bright && dotenvx run -f ../../.env.development -e MIX_ENV=dev -- mix phx.server",
"bright:test:unit:watch": "cd ./apps/bright && pnpx chokidar-cli '**/*' -i \"deps/**\" -i '_build/**' -c 'mix test --only=unit'",
"bright:act": "cd ./apps/bright && act --env MIX_ENV=test -W ./.gitea/workflows/tests.yaml --secret-file .env.development",
"test": "act -W ./.gitea/workflows/tests.yaml --secret-file .env.testing --var-file .env.testing && devbox run beep || devbox run boop",
"beep": "ffplay -nodisp -loglevel quiet -autoexit ./apps/beep/beep2.wav",
"boop": "ffplay -nodisp -loglevel quiet -autoexit ./apps/beep/beep1.wav"
}
}
}

View File

@ -1,827 +0,0 @@
{
"lockfile_version": "1",
"packages": {
"act@latest": {
"last_modified": "2025-02-07T11:26:36Z",
"resolved": "github:NixOS/nixpkgs/d98abf5cf5914e5e4e9d57205e3af55ca90ffc1d#act",
"source": "devbox-search",
"version": "0.2.72",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/69mqjq6ysm38yppm5l0a68zaxfk3jsb5-act-0.2.72",
"default": true
}
],
"store_path": "/nix/store/69mqjq6ysm38yppm5l0a68zaxfk3jsb5-act-0.2.72"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/di3cp7yr4dq07byl8hm8xwnas7hn8xcn-act-0.2.72",
"default": true
}
],
"store_path": "/nix/store/di3cp7yr4dq07byl8hm8xwnas7hn8xcn-act-0.2.72"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/yblc9543pbzncgy0q4bfdj8h7nrs35am-act-0.2.72",
"default": true
}
],
"store_path": "/nix/store/yblc9543pbzncgy0q4bfdj8h7nrs35am-act-0.2.72"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/k7nqxipi81pzfdbh2a19np9q84qmgj3w-act-0.2.72",
"default": true
}
],
"store_path": "/nix/store/k7nqxipi81pzfdbh2a19np9q84qmgj3w-act-0.2.72"
}
}
},
"caddy@latest": {
"last_modified": "2025-02-07T11:26:36Z",
"plugin_version": "0.0.3",
"resolved": "github:NixOS/nixpkgs/d98abf5cf5914e5e4e9d57205e3af55ca90ffc1d#caddy",
"source": "devbox-search",
"version": "2.9.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/wsj8iixhs5iwjhfyy4l00va0dj301sjs-caddy-2.9.1",
"default": true
}
],
"store_path": "/nix/store/wsj8iixhs5iwjhfyy4l00va0dj301sjs-caddy-2.9.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/w6cg28ws2404fglmhbzddqjjkniqfiam-caddy-2.9.1",
"default": true
}
],
"store_path": "/nix/store/w6cg28ws2404fglmhbzddqjjkniqfiam-caddy-2.9.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/8q0lw048apajs1rg6vpfbp2p1m37f25a-caddy-2.9.1",
"default": true
}
],
"store_path": "/nix/store/8q0lw048apajs1rg6vpfbp2p1m37f25a-caddy-2.9.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/ax0979vnibikl5cr180lmxvb80m2zq50-caddy-2.9.1",
"default": true
}
],
"store_path": "/nix/store/ax0979vnibikl5cr180lmxvb80m2zq50-caddy-2.9.1"
}
}
},
"chisel@latest": {
"last_modified": "2024-12-23T21:10:33Z",
"resolved": "github:NixOS/nixpkgs/de1864217bfa9b5845f465e771e0ecb48b30e02d#chisel",
"source": "devbox-search",
"version": "1.10.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/2rls3b9lq2i3g53zpr09d6ph43mgfxwz-chisel-1.10.1",
"default": true
}
],
"store_path": "/nix/store/2rls3b9lq2i3g53zpr09d6ph43mgfxwz-chisel-1.10.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/7ff1z4mr0ia2ifdgggpqkbc2j795ccy4-chisel-1.10.1",
"default": true
}
],
"store_path": "/nix/store/7ff1z4mr0ia2ifdgggpqkbc2j795ccy4-chisel-1.10.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/2cb5sa449vpah2g4q4prvqfz1dcf1rdw-chisel-1.10.1",
"default": true
}
],
"store_path": "/nix/store/2cb5sa449vpah2g4q4prvqfz1dcf1rdw-chisel-1.10.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/pphc5mnhx6mb08ak6mb3rnh061427xbj-chisel-1.10.1",
"default": true
}
],
"store_path": "/nix/store/pphc5mnhx6mb08ak6mb3rnh061427xbj-chisel-1.10.1"
}
}
},
"entr@latest": {
"last_modified": "2025-01-19T08:16:51Z",
"resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#entr",
"source": "devbox-search",
"version": "5.6",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/2qbvxdg5asmjs0fjcf84mssrv5pw7apa-entr-5.6",
"default": true
}
],
"store_path": "/nix/store/2qbvxdg5asmjs0fjcf84mssrv5pw7apa-entr-5.6"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/xnhjmgv6svqp793vxhpfcvxig5yi9s60-entr-5.6",
"default": true
}
],
"store_path": "/nix/store/xnhjmgv6svqp793vxhpfcvxig5yi9s60-entr-5.6"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/fknw34xxmp239x81r201drmlm3g9p1bb-entr-5.6",
"default": true
}
],
"store_path": "/nix/store/fknw34xxmp239x81r201drmlm3g9p1bb-entr-5.6"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/yy1d68xcy847axfmb5xws2271xkf61xk-entr-5.6",
"default": true
}
],
"store_path": "/nix/store/yy1d68xcy847axfmb5xws2271xkf61xk-entr-5.6"
}
}
},
"ffmpeg@latest": {
"last_modified": "2025-01-07T09:15:50Z",
"resolved": "github:NixOS/nixpkgs/8c9fd3e564728e90829ee7dbac6edc972971cd0f#ffmpeg",
"source": "devbox-search",
"version": "7.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "bin",
"path": "/nix/store/m48ypyjnl04mqigi6fbxa7k2c5bsvazz-ffmpeg-7.1-bin",
"default": true
},
{
"name": "man",
"path": "/nix/store/10ncvfz8srnzckq16bn14wffcx8j04ki-ffmpeg-7.1-man",
"default": true
},
{
"name": "data",
"path": "/nix/store/rqbsc9zyzi988hwcd423qdh1vh40zlzc-ffmpeg-7.1-data"
},
{
"name": "dev",
"path": "/nix/store/6ml3pn92nbmnc40s5jfpr84y65ysizk3-ffmpeg-7.1-dev"
},
{
"name": "doc",
"path": "/nix/store/ni6gri7m1pavfjxx0yfj1msvb0cfasgg-ffmpeg-7.1-doc"
},
{
"name": "lib",
"path": "/nix/store/2v2rr9dmq4grzwmg4zcsxrmbdcshks7m-ffmpeg-7.1-lib"
},
{
"name": "out",
"path": "/nix/store/nvccyp4dra8f92ywd00lk3ra121gj162-ffmpeg-7.1"
}
],
"store_path": "/nix/store/m48ypyjnl04mqigi6fbxa7k2c5bsvazz-ffmpeg-7.1-bin"
},
"aarch64-linux": {
"outputs": [
{
"name": "bin",
"path": "/nix/store/lj99z3ldzka2slgm94w2yl8cjkphn4m3-ffmpeg-7.1-bin",
"default": true
},
{
"name": "man",
"path": "/nix/store/gisda8d2pifrrjw0yirrs0x2cyn6b96i-ffmpeg-7.1-man",
"default": true
},
{
"name": "out",
"path": "/nix/store/pc332zx12j2fqf5m9mdhhs1rwksbk2fk-ffmpeg-7.1"
},
{
"name": "data",
"path": "/nix/store/4yhsn6lrfn91dk1kq9wyyd71p4hcfans-ffmpeg-7.1-data"
},
{
"name": "dev",
"path": "/nix/store/71103mmkxhzzmbj9mpzsrzbp988z3p98-ffmpeg-7.1-dev"
},
{
"name": "doc",
"path": "/nix/store/k9cc4d60frcn27zapkzhag5h9r0ig5xa-ffmpeg-7.1-doc"
},
{
"name": "lib",
"path": "/nix/store/dgcmpc8az51a4d2bxgrmfz8zlmvqxwa5-ffmpeg-7.1-lib"
}
],
"store_path": "/nix/store/lj99z3ldzka2slgm94w2yl8cjkphn4m3-ffmpeg-7.1-bin"
},
"x86_64-darwin": {
"outputs": [
{
"name": "bin",
"path": "/nix/store/z44kr817zx99vi2cc204i06cnmz83f1n-ffmpeg-7.1-bin",
"default": true
},
{
"name": "man",
"path": "/nix/store/slrha22nqrx2xw009hd37svv2nvpp26i-ffmpeg-7.1-man",
"default": true
},
{
"name": "data",
"path": "/nix/store/n3xm2rr1k1ari7ysbp9jhg65pih28x6x-ffmpeg-7.1-data"
},
{
"name": "dev",
"path": "/nix/store/59gcv4rfg38rqnazv1iq9jssjak2l3ih-ffmpeg-7.1-dev"
},
{
"name": "doc",
"path": "/nix/store/2cqa3lc75ciqzgmxjm36fd733f3gjgil-ffmpeg-7.1-doc"
},
{
"name": "lib",
"path": "/nix/store/ir47x8skj54wilcyz2x59am4y085lf6l-ffmpeg-7.1-lib"
},
{
"name": "out",
"path": "/nix/store/gqw958wdgrbfsxczqf56vnq1iffg5qv1-ffmpeg-7.1"
}
],
"store_path": "/nix/store/z44kr817zx99vi2cc204i06cnmz83f1n-ffmpeg-7.1-bin"
},
"x86_64-linux": {
"outputs": [
{
"name": "bin",
"path": "/nix/store/d1lv3x7iq129vb19ba9ph6k0yhy4fv34-ffmpeg-7.1-bin",
"default": true
},
{
"name": "man",
"path": "/nix/store/qqgvvfw2gqp47zl5v9jhdsl8mb77l909-ffmpeg-7.1-man",
"default": true
},
{
"name": "doc",
"path": "/nix/store/gmfj9af6za9i2830cgbj1qazz2nir2vs-ffmpeg-7.1-doc"
},
{
"name": "lib",
"path": "/nix/store/x8j2nr5qmikfaggl34wpm1dlldb7ik06-ffmpeg-7.1-lib"
},
{
"name": "out",
"path": "/nix/store/cx89mypnxgk0q4dibacg7dsrb4w66mv9-ffmpeg-7.1"
},
{
"name": "data",
"path": "/nix/store/5y8zk8fqpaammsnz2svgpdsayn4zlsn2-ffmpeg-7.1-data"
},
{
"name": "dev",
"path": "/nix/store/qdz8aidi7rksr4g5cpxgz9iivrlsa247-ffmpeg-7.1-dev"
}
],
"store_path": "/nix/store/d1lv3x7iq129vb19ba9ph6k0yhy4fv34-ffmpeg-7.1-bin"
}
}
},
"ggshield@latest": {
"last_modified": "2025-02-27T15:48:43Z",
"resolved": "github:NixOS/nixpkgs/6c5c5f5100281f8f4ff23f13edd17d645178c87c#ggshield",
"source": "devbox-search",
"version": "1.36.0",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/2qsc4iii58gq54l8dys7m4jw5lz0xqk0-ggshield-1.36.0",
"default": true
},
{
"name": "dist",
"path": "/nix/store/1zic4rm52c81303axs6jwpv9i5cbpgpf-ggshield-1.36.0-dist"
}
],
"store_path": "/nix/store/2qsc4iii58gq54l8dys7m4jw5lz0xqk0-ggshield-1.36.0"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/l9inx1qgmjm9hiz1fncxx15mkyqw9abc-ggshield-1.36.0",
"default": true
},
{
"name": "dist",
"path": "/nix/store/1hls55sak8fi14s6wq4ar5iiwvmzpw68-ggshield-1.36.0-dist"
}
],
"store_path": "/nix/store/l9inx1qgmjm9hiz1fncxx15mkyqw9abc-ggshield-1.36.0"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/n2s11c74rlq64w78rnnnz5j8dz72fnvs-ggshield-1.36.0",
"default": true
},
{
"name": "dist",
"path": "/nix/store/9k2svwlyx8s2ac6dnhc3sq5c3bvlyrvw-ggshield-1.36.0-dist"
}
],
"store_path": "/nix/store/n2s11c74rlq64w78rnnnz5j8dz72fnvs-ggshield-1.36.0"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/pz8s8n5qrmhfivj51932s5gqwh64c7fy-ggshield-1.36.0",
"default": true
},
{
"name": "dist",
"path": "/nix/store/i29ambqhmnizdxk9njgsyza953m5mbmn-ggshield-1.36.0-dist"
}
],
"store_path": "/nix/store/pz8s8n5qrmhfivj51932s5gqwh64c7fy-ggshield-1.36.0"
}
}
},
"git-subrepo@latest": {
"last_modified": "2025-02-07T11:26:36Z",
"resolved": "github:NixOS/nixpkgs/d98abf5cf5914e5e4e9d57205e3af55ca90ffc1d#git-subrepo",
"source": "devbox-search",
"version": "0.4.9",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/iagwf8k53inahcs16by5l7vzffbs01k4-git-subrepo-0.4.9",
"default": true
}
],
"store_path": "/nix/store/iagwf8k53inahcs16by5l7vzffbs01k4-git-subrepo-0.4.9"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/vn5qc1h596hj3s7qw1q75wzh8n8lw3im-git-subrepo-0.4.9",
"default": true
}
],
"store_path": "/nix/store/vn5qc1h596hj3s7qw1q75wzh8n8lw3im-git-subrepo-0.4.9"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/pxhc909zdsd1jxamzy762xhvvdh0lb5a-git-subrepo-0.4.9",
"default": true
}
],
"store_path": "/nix/store/pxhc909zdsd1jxamzy762xhvvdh0lb5a-git-subrepo-0.4.9"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/psy4x285pc5dk3z69gzwq7rafdhpjv5x-git-subrepo-0.4.9",
"default": true
}
],
"store_path": "/nix/store/psy4x285pc5dk3z69gzwq7rafdhpjv5x-git-subrepo-0.4.9"
}
}
},
"github:NixOS/nixpkgs/nixpkgs-unstable": {
"resolved": "github:NixOS/nixpkgs/ba0939c506a03c60a765cd7f7c43794816540eec?lastModified=1739482815&narHash=sha256-%2F5Lwtmp%2F8j%2Bro32gXzitucSdyjJ6QehfJCL58WNA7N0%3D"
},
"lazydocker@latest": {
"last_modified": "2024-12-23T21:10:33Z",
"resolved": "github:NixOS/nixpkgs/de1864217bfa9b5845f465e771e0ecb48b30e02d#lazydocker",
"source": "devbox-search",
"version": "0.24.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/5hv519gz95pwhg4vr9ka8g98bdd91mvx-lazydocker-0.24.1",
"default": true
}
],
"store_path": "/nix/store/5hv519gz95pwhg4vr9ka8g98bdd91mvx-lazydocker-0.24.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/5wb57rjn6xwyf4nc0mifgjd934i545ph-lazydocker-0.24.1",
"default": true
}
],
"store_path": "/nix/store/5wb57rjn6xwyf4nc0mifgjd934i545ph-lazydocker-0.24.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/bj056q5lmy3dx6xafwlmvhjqgxjr3awq-lazydocker-0.24.1",
"default": true
}
],
"store_path": "/nix/store/bj056q5lmy3dx6xafwlmvhjqgxjr3awq-lazydocker-0.24.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/4gwbhlb13pvdbxcb31mslkgd6pr62gyk-lazydocker-0.24.1",
"default": true
}
],
"store_path": "/nix/store/4gwbhlb13pvdbxcb31mslkgd6pr62gyk-lazydocker-0.24.1"
}
}
},
"nodejs@20": {
"last_modified": "2024-12-23T21:10:33Z",
"plugin_version": "0.0.2",
"resolved": "github:NixOS/nixpkgs/de1864217bfa9b5845f465e771e0ecb48b30e02d#nodejs_20",
"source": "devbox-search",
"version": "20.18.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/q49zbi0pzjhg4zn085h9hyz9m1k3hvpb-nodejs-20.18.1",
"default": true
},
{
"name": "libv8",
"path": "/nix/store/l08rljd2yr7i1q4x778qazcys2l4ja23-nodejs-20.18.1-libv8"
}
],
"store_path": "/nix/store/q49zbi0pzjhg4zn085h9hyz9m1k3hvpb-nodejs-20.18.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/4m1ql1hsd6kqmzr8a657qhipjgssrr0c-nodejs-20.18.1",
"default": true
},
{
"name": "libv8",
"path": "/nix/store/r8s1xalb4rpy1r44i413j84i4ny6mnsc-nodejs-20.18.1-libv8"
}
],
"store_path": "/nix/store/4m1ql1hsd6kqmzr8a657qhipjgssrr0c-nodejs-20.18.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/7ky0mn6k33731jwzhsxq6a6dsbjqamqs-nodejs-20.18.1",
"default": true
},
{
"name": "libv8",
"path": "/nix/store/jw62k3wxl38r8fcbvxaxrm696ivv3imr-nodejs-20.18.1-libv8"
}
],
"store_path": "/nix/store/7ky0mn6k33731jwzhsxq6a6dsbjqamqs-nodejs-20.18.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/j7dx1n6m5axf9r2bvly580x2ixx546wq-nodejs-20.18.1",
"default": true
},
{
"name": "libv8",
"path": "/nix/store/qljmc2skrldj0g94rib6nl4jq84193fa-nodejs-20.18.1-libv8"
}
],
"store_path": "/nix/store/j7dx1n6m5axf9r2bvly580x2ixx546wq-nodejs-20.18.1"
}
}
},
"opentofu@latest": {
"last_modified": "2025-02-07T11:26:36Z",
"resolved": "github:NixOS/nixpkgs/d98abf5cf5914e5e4e9d57205e3af55ca90ffc1d#opentofu",
"source": "devbox-search",
"version": "1.9.0",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/bk8lbpai9gckl24971cd7sy86nx65vqp-opentofu-1.9.0",
"default": true
}
],
"store_path": "/nix/store/bk8lbpai9gckl24971cd7sy86nx65vqp-opentofu-1.9.0"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/k56jwj34f3qj0j0q7rsqsnzcvjm83xfb-opentofu-1.9.0",
"default": true
}
],
"store_path": "/nix/store/k56jwj34f3qj0j0q7rsqsnzcvjm83xfb-opentofu-1.9.0"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/q8a4niqvlph0gbch407q19v444qyflpi-opentofu-1.9.0",
"default": true
}
],
"store_path": "/nix/store/q8a4niqvlph0gbch407q19v444qyflpi-opentofu-1.9.0"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/1gc7dl8bcrks1y98qlblr08aly4b3qry-opentofu-1.9.0",
"default": true
}
],
"store_path": "/nix/store/1gc7dl8bcrks1y98qlblr08aly4b3qry-opentofu-1.9.0"
}
}
},
"python310@latest": {
"last_modified": "2025-02-07T11:26:36Z",
"plugin_version": "0.0.4",
"resolved": "github:NixOS/nixpkgs/d98abf5cf5914e5e4e9d57205e3af55ca90ffc1d#python310",
"source": "devbox-search",
"version": "3.10.16",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/n3bv4blh1n8xcswpn23d8rkdf19z1z67-python3-3.10.16",
"default": true
}
],
"store_path": "/nix/store/n3bv4blh1n8xcswpn23d8rkdf19z1z67-python3-3.10.16"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/wdkg0yc6h9fwvvqwh3zss1a2rz4mrmgl-python3-3.10.16",
"default": true
},
{
"name": "debug",
"path": "/nix/store/wxnpshdg4kbdmpl44qnqx29i0pszsa7f-python3-3.10.16-debug"
}
],
"store_path": "/nix/store/wdkg0yc6h9fwvvqwh3zss1a2rz4mrmgl-python3-3.10.16"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/40lamqbgh1g2x9mml635n329h4gh1928-python3-3.10.16",
"default": true
}
],
"store_path": "/nix/store/40lamqbgh1g2x9mml635n329h4gh1928-python3-3.10.16"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/8757y4bf8d77z7c3pjz1nlwi2xlkk93h-python3-3.10.16",
"default": true
},
{
"name": "debug",
"path": "/nix/store/4rj9krr4hgv14rjlsrlajksi580fg4np-python3-3.10.16-debug"
}
],
"store_path": "/nix/store/8757y4bf8d77z7c3pjz1nlwi2xlkk93h-python3-3.10.16"
}
}
},
"python310Packages.pip@latest": {
"last_modified": "2023-12-13T22:54:10Z",
"resolved": "github:NixOS/nixpkgs/fd04bea4cbf76f86f244b9e2549fca066db8ddff#python310Packages.pip",
"source": "devbox-search",
"version": "23.2.1",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/vjyrxbxqsadvr9g6mzig6y406dhwcrqi-python3.10-pip-23.2.1",
"default": true
},
{
"name": "man",
"path": "/nix/store/2ra46imgas5srgxx8mgs424akh5j1msv-python3.10-pip-23.2.1-man",
"default": true
},
{
"name": "dist",
"path": "/nix/store/xjj6xjh21k08rkyg6hnkbyrygqhqgn8y-python3.10-pip-23.2.1-dist"
}
],
"store_path": "/nix/store/vjyrxbxqsadvr9g6mzig6y406dhwcrqi-python3.10-pip-23.2.1"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/j8pxwv7vyjm8z2fqglijjvabbkmxbv9r-python3.10-pip-23.2.1",
"default": true
},
{
"name": "man",
"path": "/nix/store/bcfliw00z123ddyi8hsfxvfn2npdcpdq-python3.10-pip-23.2.1-man",
"default": true
},
{
"name": "dist",
"path": "/nix/store/2326b2sr9p2z9bsghd2pzs346g2qjn7f-python3.10-pip-23.2.1-dist"
}
],
"store_path": "/nix/store/j8pxwv7vyjm8z2fqglijjvabbkmxbv9r-python3.10-pip-23.2.1"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/7lyvqf8wl47wzgsqmlcz39ycmwxyg9zx-python3.10-pip-23.2.1",
"default": true
},
{
"name": "man",
"path": "/nix/store/0j02s5hbsdhfxvvay5dm9j68nhalm39v-python3.10-pip-23.2.1-man",
"default": true
},
{
"name": "dist",
"path": "/nix/store/8dvqh9ai83lqaaixs3bhmf1n4jxgp4v7-python3.10-pip-23.2.1-dist"
}
],
"store_path": "/nix/store/7lyvqf8wl47wzgsqmlcz39ycmwxyg9zx-python3.10-pip-23.2.1"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/gdhvfi5zaqzpa5l3kk0spmv71r549slf-python3.10-pip-23.2.1",
"default": true
},
{
"name": "man",
"path": "/nix/store/p37y2fhm6l3nd624jmr2r680wf4p0544-python3.10-pip-23.2.1-man",
"default": true
},
{
"name": "dist",
"path": "/nix/store/r4qj7psryk532rkh0srhn25ygv8yk541-python3.10-pip-23.2.1-dist"
}
],
"store_path": "/nix/store/gdhvfi5zaqzpa5l3kk0spmv71r549slf-python3.10-pip-23.2.1"
}
}
},
"yt-dlp@latest": {
"last_modified": "2025-01-03T14:51:55Z",
"resolved": "github:NixOS/nixpkgs/a27871180d30ebee8aa6b11bf7fef8a52f024733#yt-dlp",
"source": "devbox-search",
"version": "2024.12.23",
"systems": {
"aarch64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/8fmy039278i7q31zyyrfmr4z5k35xvp6-yt-dlp-2024.12.23",
"default": true
},
{
"name": "dist",
"path": "/nix/store/v9mplambvs7hlcpcpnvmf7c30pk5cx8d-yt-dlp-2024.12.23-dist"
}
],
"store_path": "/nix/store/8fmy039278i7q31zyyrfmr4z5k35xvp6-yt-dlp-2024.12.23"
},
"aarch64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/wzna2q40a81qj4icma4q20q9fjg9md76-yt-dlp-2024.12.23",
"default": true
},
{
"name": "dist",
"path": "/nix/store/z0b0nv6a2wl0565f5ffrzhg2zcqflvrl-yt-dlp-2024.12.23-dist"
}
],
"store_path": "/nix/store/wzna2q40a81qj4icma4q20q9fjg9md76-yt-dlp-2024.12.23"
},
"x86_64-darwin": {
"outputs": [
{
"name": "out",
"path": "/nix/store/mpbp9jar45hk22ddldm0bbk60vz5w643-yt-dlp-2024.12.23",
"default": true
},
{
"name": "dist",
"path": "/nix/store/gq4l9yfs7fjb05qkl97hmsq69s3k4shr-yt-dlp-2024.12.23-dist"
}
],
"store_path": "/nix/store/mpbp9jar45hk22ddldm0bbk60vz5w643-yt-dlp-2024.12.23"
},
"x86_64-linux": {
"outputs": [
{
"name": "out",
"path": "/nix/store/wqcs5crm36p603fkh9j2k868zzb8q79c-yt-dlp-2024.12.23",
"default": true
},
{
"name": "dist",
"path": "/nix/store/j452c5bii2slx1biix7lfc5k0aqg2rvs-yt-dlp-2024.12.23-dist"
}
],
"store_path": "/nix/store/wqcs5crm36p603fkh9j2k868zzb8q79c-yt-dlp-2024.12.23"
}
}
}
}
}

View File

@ -10,12 +10,12 @@
"keywords": [],
"author": "@CJ_Clippy",
"license": "Unlicense",
"packageManager": "pnpm@9.6.0",
"dependencies": {
"chokidar-cli": "^3.0.0",
"types": "^0.1.1"
},
"devDependencies": {
"concurrently": "^8.2.2"
}
},
"packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad"
}

View File

@ -1,3 +1,6 @@
runs
requirements.txt
venv
src/test
node_modules

View File

@ -1,3 +1,5 @@
runs
venv
vibeui
generated

View File

@ -58,6 +58,7 @@
"@types/node": "^22.16.3",
"@types/node-fetch": "^2.6.12",
"cache-manager": "^7.0.1",
"canvas": "^3.1.2",
"chokidar-cli": "^3.0.0",
"concurrently": "^9.2.0",
"date-fns": "^4.1.0",
@ -77,6 +78,7 @@
"nanoid": "^5.1.5",
"node-fetch": "^3.3.2",
"onnxruntime-node": "1.22.0-rev",
"protobufjs": "^7.5.3",
"rate-limiter-flexible": "^7.1.1",
"rimraf": "6.0.1",
"sharp": "^0.34.3",

View File

@ -71,6 +71,9 @@ importers:
cache-manager:
specifier: ^7.0.1
version: 7.0.1
canvas:
specifier: ^3.1.2
version: 3.1.2
chokidar-cli:
specifier: ^3.0.0
version: 3.0.0
@ -128,6 +131,9 @@ importers:
onnxruntime-node:
specifier: 1.22.0-rev
version: 1.22.0-rev
protobufjs:
specifier: ^7.5.3
version: 7.5.3
rate-limiter-flexible:
specifier: ^7.1.1
version: 7.1.1
@ -956,6 +962,36 @@ packages:
'@prisma/get-platform@6.8.2':
resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==}
'@protobufjs/aspromise@1.1.2':
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
'@protobufjs/base64@1.1.2':
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
'@protobufjs/codegen@2.0.4':
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
'@protobufjs/eventemitter@1.1.0':
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
'@protobufjs/fetch@1.1.0':
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
'@protobufjs/float@1.0.2':
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
'@protobufjs/inquire@1.1.0':
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
'@protobufjs/path@1.1.2':
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
'@protobufjs/pool@1.1.0':
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@rollup/rollup-android-arm-eabi@4.45.1':
resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==}
cpu: [arm]
@ -1640,6 +1676,10 @@ packages:
canvas-renderer@2.2.1:
resolution: {integrity: sha512-RrBgVL5qCEDIXpJ6NrzyRNoTnXxYarqm/cS/W6ERhUJts5UQtt/XPEosGN3rqUkZ4fjBArlnCbsISJ+KCFnIAg==}
canvas@3.1.2:
resolution: {integrity: sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==}
engines: {node: ^18.12.0 || >= 20.9.0}
chai@5.2.1:
resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==}
engines: {node: '>=18'}
@ -2427,6 +2467,9 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
loupe@3.1.4:
resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==}
@ -2563,6 +2606,9 @@ packages:
node-addon-api@6.1.0:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-addon-api@8.5.0:
resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==}
engines: {node: ^18 || ^20 || >= 21}
@ -2868,6 +2914,10 @@ packages:
process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
protobufjs@7.5.3:
resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==}
engines: {node: '>=12.0.0'}
pstree.remy@1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
@ -4645,6 +4695,29 @@ snapshots:
dependencies:
'@prisma/debug': 6.8.2
'@protobufjs/aspromise@1.1.2': {}
'@protobufjs/base64@1.1.2': {}
'@protobufjs/codegen@2.0.4': {}
'@protobufjs/eventemitter@1.1.0': {}
'@protobufjs/fetch@1.1.0':
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/float@1.0.2': {}
'@protobufjs/inquire@1.1.0': {}
'@protobufjs/path@1.1.2': {}
'@protobufjs/pool@1.1.0': {}
'@protobufjs/utf8@1.1.0': {}
'@rollup/rollup-android-arm-eabi@4.45.1':
optional: true
@ -5422,6 +5495,11 @@ snapshots:
dependencies:
'@types/node': 22.16.4
canvas@3.1.2:
dependencies:
node-addon-api: 7.1.1
prebuild-install: 7.1.3
chai@5.2.1:
dependencies:
assertion-error: 2.0.1
@ -6289,6 +6367,8 @@ snapshots:
lodash@4.17.21: {}
long@5.3.2: {}
loupe@3.1.4: {}
lru-cache@10.4.3: {}
@ -6396,6 +6476,8 @@ snapshots:
node-addon-api@6.1.0: {}
node-addon-api@7.1.1: {}
node-addon-api@8.5.0: {}
node-domexception@1.0.0: {}
@ -6686,6 +6768,21 @@ snapshots:
process-warning@5.0.0: {}
protobufjs@7.5.3:
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/base64': 1.1.2
'@protobufjs/codegen': 2.0.4
'@protobufjs/eventemitter': 1.1.0
'@protobufjs/fetch': 1.1.0
'@protobufjs/float': 1.0.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/node': 22.16.4
long: 5.3.2
pstree.remy@1.1.8: {}
pump@3.0.3:

View File

@ -0,0 +1 @@
ultralytics

View File

@ -8,6 +8,7 @@ dotenvx.config({ path: ['../../.env.development'] })
const EnvSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
VENV: z.string(),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
ORIGIN: z.string(),
@ -27,6 +28,7 @@ const EnvSchema = z.object({
CDN_TOKEN_SECRET: z.string(),
CACHE_ROOT: z.string().default('/tmp/our'),
VIBEUI_DIR: z.string().default('/opt/futureporn/apps/vibeui'),
APP_DIR: z.string().default('/app')
});
const parsed = EnvSchema.safeParse(process.env);

View File

@ -60,48 +60,48 @@ async function loadDataYaml(yamlPath: string): Promise<DataYaml> {
}
// async function preparePython(helpers) {
// const spawn = await getNanoSpawn();
// const venvPath = join(env.VIBEUI_DIR, "venv");
async function preparePython(helpers) {
const spawn = await getNanoSpawn();
const venvPath = join(env.VIBEUI_DIR, "venv");
// // Determine Python executable
// let pythonCmd;
// try {
// pythonCmd = which.sync("python3");
// } catch {
// helpers.logger.error("Python is not installed or not in PATH.");
// throw new Error("Python not found in PATH.");
// }
// Determine Python executable
let pythonCmd;
try {
pythonCmd = which.sync("python3");
} catch {
helpers.logger.error("Python is not installed or not in PATH.");
throw new Error("Python not found in PATH.");
}
// // If venv doesn't exist, create it
// if (!existsSync(venvPath)) {
// helpers.logger.info("Python venv not found. Creating one...");
// If venv doesn't exist, create it
if (!existsSync(venvPath)) {
helpers.logger.info("Python venv not found. Creating one...");
// try {
// await spawn(pythonCmd, ["-m", "venv", "venv"], {
// cwd: env.VIBEUI_DIR,
// });
try {
await spawn(pythonCmd, ["-m", "venv", "venv"], {
cwd: env.VIBEUI_DIR,
});
// helpers.logger.info("Python venv successfully created.");
// } catch (err) {
// helpers.logger.error("Failed to create Python venv:", err);
helpers.logger.info("Python venv successfully created.");
} catch (err) {
helpers.logger.error("Failed to create Python venv:", err);
// // Clean up partially created venv if needed
// try {
// if (existsSync(venvPath)) {
// rmSync(venvPath, { recursive: true, force: true });
// helpers.logger.warn("Removed broken venv directory.");
// }
// } catch (cleanupErr) {
// helpers.logger.error("Error while cleaning up broken venv:", cleanupErr);
// }
// Clean up partially created venv if needed
try {
if (existsSync(venvPath)) {
rmSync(venvPath, { recursive: true, force: true });
helpers.logger.warn("Removed broken venv directory.");
}
} catch (cleanupErr) {
helpers.logger.error("Error while cleaning up broken venv:", cleanupErr);
}
// throw new Error("Python venv creation failed. Check if python3 and python3-venv are installed.");
// }
// } else {
// helpers.logger.info("Using existing Python venv.");
// }
// }
throw new Error("Python venv creation failed. Check if python3 and python3-venv are installed.");
}
} else {
helpers.logger.info("Using existing Python venv.");
}
}
async function ffprobe(videoPath: string): Promise<{ fps: number; frames: number }> {

View File

@ -1,18 +1,54 @@
import type { Task, Helpers } from "graphile-worker";
import { PrismaClient } from "../../generated/prisma";
import { withAccelerate } from "@prisma/extension-accelerate";
import { getOrDownloadAsset } from "../utils/cache";
import { env } from "../config/env";
import { getS3Client, uploadFile } from "../utils/s3";
import { buildFunscript } from '../utils/funscripts';
import { vibeuiInference } from "../utils/vibeui";
import { join } from "node:path";
import { nanoid } from "nanoid";
import { existsSync, rmSync } from "node:fs";
import { join, basename, extname } from "node:path";
import { readFile, writeFile, readdir } from 'node:fs/promises';
import yaml from 'js-yaml';
import { getNanoSpawn } from "../utils/nanoSpawn";
import which from "which";
import * as ort from 'onnxruntime-node';
import { inference, loadDataYaml } from "../utils/vibeui";
import { ffprobe } from "../utils/vibeui";
import { preparePython } from "../utils/python";
import { generateActions2, buildFunscript } from "../utils/funscripts";
interface Payload {
vodId: string;
}
// interface Detection {
// startFrame: number;
// endFrame: number;
// className: string;
// }
// interface DataYaml {
// path: string;
// train: string;
// val: string;
// names: Record<string, string>;
// }
// interface FunscriptAction {
// at: number;
// pos: number;
// }
// interface Funscript {
// version: string;
// actions: FunscriptAction[];
// }
// interface ClassPositionMap {
// [className: string]: number | 'pattern';
// }
const prisma = new PrismaClient().$extends(withAccelerate());
@ -24,6 +60,209 @@ function assertPayload(payload: any): asserts payload is Payload {
}
// export async function buildFunscript(
// helpers: Helpers,
// predictionOutput: string,
// videoPath: string
// ): Promise<string> {
// const labelDir = join(predictionOutput, 'labels');
// const yamlPath = join(predictionOutput, 'data.yaml');
// const outputPath = join(process.env.CACHE_ROOT ?? '/tmp', `${nanoid()}.funscript`);
// helpers.logger.info('Starting Funscript generation');
// try {
// const data = await loadDataYaml(join(env.VIBEUI_DIR, 'data.yaml'))
// const classPositionMap = await loadClassPositionMap(data, helpers);
// const { fps, totalFrames } = await loadVideoMetadata(videoPath, helpers);
// const detectionSegments = await processLabelFiles(labelDir, helpers, data);
// const totalDurationMs = Math.floor((totalFrames / fps) * 1000);
// const actions = generateActions2(totalDurationMs, fps, detectionSegments, classPositionMap);
// await writeFunscript(outputPath, actions, helpers);
// return outputPath;
// } catch (error) {
// helpers.logger.error(`Error generating Funscript: ${error instanceof Error ? error.message : 'Unknown error'}`);
// throw error;
// }
// }
// async function loadClassPositionMap(data: DataYaml, helpers: Helpers): Promise<ClassPositionMap> {
// try {
// if (
// !data ||
// typeof data !== 'object' ||
// !('names' in data) ||
// typeof data.names !== 'object' ||
// data.names === null ||
// Object.keys(data.names).length === 0
// ) {
// throw new Error('Invalid data.yaml: "names" field is missing, not an object, or empty');
// }
// const positionMap: ClassPositionMap = {
// ControlledByTipper: 50,
// ControlledByTipperHigh: 80,
// ControlledByTipperLow: 20,
// ControlledByTipperMedium: 50,
// ControlledByTipperUltrahigh: 95,
// Ring1: 30,
// Ring2: 40,
// Ring3: 50,
// Ring4: 60,
// Earthquake: 'pattern',
// Fireworks: 'pattern',
// Pulse: 'pattern',
// Wave: 'pattern',
// Pause: 0,
// RandomTime: 70,
// HighLevel: 80,
// LowLevel: 20,
// MediumLevel: 50,
// UltraHighLevel: 95
// };
// const names = Object.values(data.names);
// for (const name of names) {
// if (typeof name !== 'string' || name.trim() === '') {
// helpers.logger.info(`Skipping invalid class name: ${name}`);
// continue;
// }
// if (!(name in positionMap)) {
// helpers.logger.info(`No position mapping for class "${name}", defaulting to 0`);
// positionMap[name] = 0;
// }
// }
// helpers.logger.info(`Loaded class position map: ${JSON.stringify(positionMap)}`);
// return positionMap;
// } catch (error) {
// helpers.logger.error(`Error loading data.yaml: ${error instanceof Error ? error.message : 'Unknown error'}`);
// throw error;
// }
// }
// function generatePatternPositions(startMs: number, durationMs: number, className: string, fps: number): FunscriptAction[] {
// const actions: FunscriptAction[] = [];
// const frameDurationMs = 1000 / fps;
// const totalFrames = Math.floor(durationMs / frameDurationMs);
// const intervalMs = 100;
// for (let timeMs = 0; timeMs < durationMs; timeMs += intervalMs) {
// const progress = timeMs / durationMs;
// let pos = 0;
// switch (className) {
// case 'Pulse':
// pos = Math.round(50 * Math.sin(progress * 2 * Math.PI));
// break;
// case 'Wave':
// pos = Math.round(50 + 50 * Math.sin(progress * 2 * Math.PI));
// break;
// case 'Fireworks':
// pos = Math.random() > 0.5 ? 80 : 0;
// break;
// case 'Earthquake':
// pos = Math.round(90 * Math.sin(progress * 4 * Math.PI) + (Math.random() - 0.5) * 10);
// pos = Math.max(0, Math.min(90, pos));
// break;
// }
// actions.push({ at: startMs + timeMs, pos });
// }
// return actions;
// }
// async function loadVideoMetadata(videoPath: string, helpers: Helpers) {
// const { fps, frames: totalFrames } = await ffprobe(videoPath);
// helpers.logger.info(`Video metadata: fps=${fps}, frames=${totalFrames}`);
// return { fps, totalFrames };
// }
// async function processLabelFiles(labelDir: string, helpers: Helpers, data: DataYaml): Promise<Detection[]> {
// const labelFiles = (await readdir(labelDir)).filter(file => file.endsWith('.txt'));
// const detections: Map<number, Detection[]> = new Map();
// const names = data.names;
// for (const file of labelFiles) {
// const match = file.match(/(\d+)\.txt$/);
// if (!match) {
// helpers.logger.info(`Skipping invalid filename: ${file}`);
// continue;
// }
// const frameIndex = parseInt(match[1], 10);
// if (isNaN(frameIndex)) {
// helpers.logger.info(`Skipping invalid frame index from filename: ${file}`);
// continue;
// }
// const content = await readFile(join(labelDir, file), 'utf8');
// const lines = content.trim().split('\n');
// const frameDetections: Detection[] = [];
// let maxConfidence = 0;
// let selectedClassIndex = -1;
// for (const line of lines) {
// const parts = line.trim().split(/\s+/);
// if (parts.length < 6) continue;
// const classIndex = parseInt(parts[0], 10);
// const confidence = parseFloat(parts[5]);
// if (isNaN(classIndex) || isNaN(confidence)) continue;
// if (confidence >= 0.7 && confidence > maxConfidence) {
// maxConfidence = confidence;
// selectedClassIndex = classIndex;
// }
// }
// if (maxConfidence > 0) {
// const className = (data.names as Record<string, string>)[selectedClassIndex.toString()];
// if (className) {
// frameDetections.push({ startFrame: frameIndex, endFrame: frameIndex, className });
// }
// }
// if (frameDetections.length > 0) {
// detections.set(frameIndex, frameDetections);
// }
// }
// // Merge overlapping detections into continuous segments
// const detectionSegments: Detection[] = [];
// let currentDetection: Detection | null = null;
// for (const [frameIndex, frameDetections] of detections.entries()) {
// for (const detection of frameDetections) {
// if (!currentDetection || currentDetection.className !== detection.className) {
// if (currentDetection) detectionSegments.push(currentDetection);
// currentDetection = { ...detection, endFrame: frameIndex };
// } else {
// currentDetection.endFrame = frameIndex;
// }
// }
// }
// if (currentDetection) detectionSegments.push(currentDetection);
// return detectionSegments;
// }
// async function writeFunscript(outputPath: string, actions: FunscriptAction[], helpers: Helpers) {
// const funscript: Funscript = { version: '1.0', actions };
// await writeFile(outputPath, JSON.stringify(funscript, null, 2));
// helpers.logger.info(`Funscript generated: ${outputPath} (${actions.length} actions)`);
// }
const createFunscript: Task = async (payload: any, helpers: Helpers) => {
assertPayload(payload);
const { vodId } = payload;
@ -31,6 +270,7 @@ const createFunscript: Task = async (payload: any, helpers: Helpers) => {
const vod = await prisma.vod.findFirstOrThrow({ where: { id: vodId } });
await preparePython()
if (vod.funscript) {
helpers.logger.info(`Doing nothing-- vod ${vodId} already has a funscript.`);
@ -51,12 +291,11 @@ const createFunscript: Task = async (payload: any, helpers: Helpers) => {
helpers.logger.info(`Creating funscript for vod ${vodId}...`);
const modelPath = join(env.VIBEUI_DIR, 'vibeui.onnx')
const predictionOutput = await vibeuiInference(modelPath, videoFilePath);
helpers.logger.info(`prediction output ${predictionOutput}`);
const predictionOutputPath = await inference(videoFilePath);
helpers.logger.info(`prediction output ${predictionOutputPath}`);
const funscriptFilePath = await buildFunscript(predictionOutput, videoFilePath)
const funscriptFilePath = await buildFunscript(predictionOutputPath, videoFilePath)
const s3Key = `funscripts/${vodId}.funscript`;

View File

@ -0,0 +1,75 @@
import type { Task, Helpers } from "graphile-worker";
import { PrismaClient } from "../../generated/prisma";
import { withAccelerate } from "@prisma/extension-accelerate";
import { getOrDownloadAsset } from "../utils/cache";
import { env } from "../config/env";
import { getS3Client, uploadFile } from "../utils/s3";
import { buildFunscript } from '../utils/funscripts';
import { getModelClasses, vibeuiInference } from "../utils/vibeui";
import { join } from "node:path";
interface Payload {
vodId: string;
}
const prisma = new PrismaClient().$extends(withAccelerate());
function assertPayload(payload: any): asserts payload is Payload {
if (typeof payload !== "object" || !payload) throw new Error("invalid payload-- was not an object.");
if (typeof payload.vodId !== "string") throw new Error("invalid payload-- was missing vodId");
}
const createFunscript: Task = async (payload: any, helpers: Helpers) => {
assertPayload(payload);
const { vodId } = payload;
const vod = await prisma.vod.findFirstOrThrow({ where: { id: vodId } });
if (vod.funscript) {
helpers.logger.info(`Doing nothing-- vod ${vodId} already has a funscript.`);
return;
}
if (!vod.sourceVideo) {
const msg = `Cannot create funscript: Vod ${vodId} is missing a source video.`;
helpers.logger.warn(msg);
throw new Error(msg);
}
const s3Client = getS3Client();
const videoFilePath = await getOrDownloadAsset(s3Client, env.S3_BUCKET, vod.sourceVideo);
helpers.logger.info(`Downloaded video to ${videoFilePath}`);
helpers.logger.info(`Creating funscript for vod ${vodId}...`);
const modelPath = join(env.VIBEUI_DIR, 'vibeui.onnx')
const predictionOutput = await vibeuiInference(modelPath, videoFilePath);
helpers.logger.info(`prediction output ${predictionOutput}`);
const classes = await getModelClasses(modelPath)
const funscriptFilePath = await buildFunscript(classes, predictionOutput, videoFilePath)
const s3Key = `funscripts/${vodId}.funscript`;
const s3Url = await uploadFile(s3Client, env.S3_BUCKET, s3Key, funscriptFilePath, "application/json");
helpers.logger.info(`Uploaded funscript to S3: ${s3Url}`);
await prisma.vod.update({
where: { id: vodId },
data: { funscript: s3Key }
});
helpers.logger.info(`Funscript saved to database for vod ${vodId}`);
};
export default createFunscript;

View File

@ -0,0 +1,141 @@
import { describe, it, expect, beforeAll } from 'vitest';
import { join } from 'node:path';
import { readdir, readFile } from 'node:fs/promises';
import { loadDataYaml, loadVideoMetadata, inference } from '../utils/vibeui';
import { env } from '../config/env';
import { DataYaml, writeFunscript, generateActions1, classPositionMap, buildFunscript } from '../utils/funscripts';
import { nanoid } from 'nanoid';
import { getNanoSpawn } from '../utils/nanoSpawn';
import { preparePython } from '../utils/python';
interface Detection {
startFrame: number;
endFrame: number;
className: string;
}
const __dirname = import.meta.dirname;
const fixturesDir = join(__dirname, '..', 'tests', 'fixtures')
const sampleVideoPath = join(fixturesDir, 'sample.mp4')
/**
* Processes YOLO label files and constructs time-aligned detection segments.
*
* - Reads all `.txt` label files from a directory, one per video frame.
* - Parses bounding box class indices and confidence scores.
* - Selects the most confident detection per frame above a confidence threshold (0.7).
* - Maps class indices to class names using the provided `data.yaml`.
* - Groups consecutive frames with the same class into continuous detection segments.
*
* @param labelDir - Path to the directory containing YOLO label `.txt` files.
* @param data - Parsed data.yaml object containing class name mappings.
* @returns An array of merged `Detection` objects with start and end frame ranges.
* @throws If file reading or parsing fails.
*/
async function processLabelFiles(labelDir: string, data: DataYaml): Promise<Detection[]> {
const labelFiles = (await readdir(labelDir)).filter(file => file.endsWith('.txt'));
console.log("Label files found:", labelFiles);
const detections: Map<number, Detection[]> = new Map();
const names = data.names;
for (const file of labelFiles) {
const match = file.match(/(\d+)\.txt$/);
if (!match) {
console.log(`Skipping invalid filename: ${file}`);
continue;
}
const frameIndex = parseInt(match[1], 10);
if (isNaN(frameIndex)) {
console.log(`Skipping invalid frame index from filename: ${file}`);
continue;
}
const content = await readFile(join(labelDir, file), 'utf8');
const lines = content.trim().split('\n');
const frameDetections: Detection[] = [];
let maxConfidence = 0;
let selectedClassIndex = -1;
for (const line of lines) {
const parts = line.trim().split(/\s+/);
if (parts.length < 6) continue;
const classIndex = parseInt(parts[0], 10);
const confidence = parseFloat(parts[5]);
if (isNaN(classIndex) || isNaN(confidence)) continue;
if (confidence >= 0.7 && confidence > maxConfidence) {
maxConfidence = confidence;
selectedClassIndex = classIndex;
}
}
if (maxConfidence > 0) {
const className = (data.names as Record<string, string>)[selectedClassIndex.toString()];
if (className) {
frameDetections.push({ startFrame: frameIndex, endFrame: frameIndex, className });
}
}
if (frameDetections.length > 0) {
detections.set(frameIndex, frameDetections);
}
}
// Merge overlapping detections into continuous segments
const detectionSegments: Detection[] = [];
let currentDetection: Detection | null = null;
for (const [frameIndex, frameDetections] of detections.entries()) {
for (const detection of frameDetections) {
if (!currentDetection || currentDetection.className !== detection.className) {
if (currentDetection) detectionSegments.push(currentDetection);
currentDetection = { ...detection, endFrame: frameIndex };
} else {
currentDetection.endFrame = frameIndex;
}
}
}
if (currentDetection) detectionSegments.push(currentDetection);
return detectionSegments;
}
describe('createFunscript', () => {
let predictionOutputPath: string
beforeAll(async () => {
predictionOutputPath = await inference(sampleVideoPath)
}, 35_000)
it('should contain positions other than 0', async () => {
const videoPath = join(__dirname, 'fixtures', 'sample.mp4');
const funscriptPath = await buildFunscript(predictionOutputPath, videoPath);
console.log(`built funscript at ${funscriptPath}`)
const content = JSON.parse(await readFile(funscriptPath, 'utf8') as string);
expect(content).toHaveProperty('version', '1.0');
expect(Array.isArray(content.actions)).toBe(true);
const allPosZero = content.actions.every((a: any) => a.pos === 0);
expect(allPosZero).toBe(false); // if this fails, it means the funscript did not generate position values that map to the various detected classes
expect(content.actions.length).toBeGreaterThan(0);
})
})

View File

@ -1 +0,0 @@
54 0.038431 0.089941 0.063310 0.075142 53.145020

View File

@ -1 +0,0 @@
53 0.038782 0.087836 0.061486 0.073967 52.052288

View File

@ -1 +0,0 @@
56 0.035719 0.076428 0.052471 0.062387 45.073280

View File

@ -1 +0,0 @@
55 0.035468 0.077997 0.051584 0.064488 45.364609

View File

@ -1 +0,0 @@
49 0.037174 0.075309 0.052773 0.061913 42.606949

View File

@ -1 +0,0 @@
49 0.036868 0.075512 0.051822 0.062220 42.845398

View File

@ -1 +0,0 @@
58 0.033982 0.077916 0.052823 0.061902 49.985241

View File

@ -1 +0,0 @@
55 0.031318 0.063680 0.047338 0.051505 42.080074

View File

@ -1 +0,0 @@
49 0.032635 0.051113 0.043934 0.045176 29.531828

View File

@ -1 +0,0 @@
59 0.029333 0.040158 0.037184 0.046115 42.523987

View File

@ -1 +0,0 @@
60 0.023820 0.041723 0.024071 0.052091 51.896393

View File

@ -1 +0,0 @@
59 0.028336 0.039560 0.034117 0.048055 45.588940

View File

@ -1 +0,0 @@
54 0.040177 0.089766 0.063404 0.075716 52.578529

View File

@ -1 +0,0 @@
52 0.030120 0.048511 0.038019 0.047205 38.291862

View File

@ -1 +0,0 @@
51 0.028028 0.048727 0.035394 0.045787 41.533302

View File

@ -1 +0,0 @@
53 0.030124 0.056849 0.042351 0.057114 45.306530

View File

@ -1 +0,0 @@
53 0.031095 0.054593 0.043671 0.062581 45.146286

View File

@ -1 +0,0 @@
55 0.032167 0.056456 0.045219 0.064320 46.130051

View File

@ -1 +0,0 @@
46 0.022684 0.037057 0.020501 0.039577 37.016544

View File

@ -1 +0,0 @@
44 0.020022 0.036946 0.020763 0.039563 37.264973

View File

@ -1 +0,0 @@
46 0.023001 0.038526 0.025594 0.027946 25.011108

View File

@ -1 +0,0 @@
45 0.021071 0.038914 0.026670 0.028368 23.412777

View File

@ -1 +0,0 @@
34 0.022739 0.043524 0.029497 0.030117 21.047352

View File

@ -1 +0,0 @@
54 0.038738 0.088193 0.061146 0.073904 52.342270

View File

@ -1 +0,0 @@
33 0.023468 0.047506 0.032993 0.033216 22.122324

View File

@ -1 +0,0 @@
29 0.024802 0.050099 0.036248 0.036333 23.996782

View File

@ -1 +0,0 @@
27 0.026263 0.054922 0.039299 0.039462 25.030342

View File

@ -1 +0,0 @@
27 0.027968 0.059208 0.042927 0.041978 26.387854

View File

@ -1 +0,0 @@
27 0.026905 0.057451 0.040628 0.041066 26.058964

View File

@ -1 +0,0 @@
28 0.027567 0.057046 0.041958 0.042930 27.190252

View File

@ -1 +0,0 @@
28 0.027424 0.058000 0.041726 0.043075 27.314507

View File

@ -1 +0,0 @@
28 0.027354 0.059303 0.041741 0.043003 27.271029

View File

@ -1 +0,0 @@
28 0.027216 0.060089 0.041448 0.042873 27.201962

View File

@ -1 +0,0 @@
28 0.027259 0.059818 0.041481 0.042875 27.166683

View File

@ -1 +0,0 @@
55 0.038905 0.088440 0.061114 0.074878 52.844406

View File

@ -1 +0,0 @@
28 0.027364 0.058919 0.041654 0.043002 27.260029

View File

@ -1 +0,0 @@
28 0.027422 0.059680 0.041713 0.043012 27.289568

View File

@ -1 +0,0 @@
28 0.027505 0.060784 0.041890 0.042989 27.195068

View File

@ -1 +0,0 @@
28 0.027367 0.061949 0.041727 0.043011 27.209845

View File

@ -1 +0,0 @@
28 0.027787 0.063247 0.042555 0.043069 27.263746

View File

@ -1 +0,0 @@
31 0.029057 0.066847 0.045142 0.044002 27.712589

View File

@ -1 +0,0 @@
47 0.038125 0.073041 0.063277 0.069168 46.141243

View File

@ -1 +0,0 @@
48 0.038465 0.077648 0.063928 0.070589 47.159187

View File

@ -1 +0,0 @@
48 0.038857 0.077233 0.064686 0.071919 47.977119

View File

@ -1 +0,0 @@
49 0.039132 0.078201 0.065272 0.072841 48.427032

View File

@ -1 +0,0 @@
56 0.041007 0.094351 0.065533 0.077863 53.407448

View File

@ -1 +0,0 @@
49 0.039132 0.079005 0.065348 0.073051 48.494751

View File

@ -1 +0,0 @@
48 0.039090 0.075240 0.065312 0.072294 47.814663

View File

@ -1 +0,0 @@
48 0.039028 0.076693 0.065173 0.072094 47.912033

View File

@ -1 +0,0 @@
48 0.038899 0.073056 0.064984 0.071549 47.317184

View File

@ -1 +0,0 @@
47 0.038281 0.067039 0.063682 0.068646 45.645699

View File

@ -1 +0,0 @@
47 0.037488 0.066094 0.062121 0.068092 46.430637

View File

@ -1 +0,0 @@
43 0.036806 0.065111 0.060806 0.064199 42.797428

View File

@ -1 +0,0 @@
43 0.036809 0.068780 0.060680 0.063662 42.322411

View File

@ -1 +0,0 @@
41 0.036440 0.072136 0.059879 0.061923 40.799137

View File

@ -1 +0,0 @@
48 0.036187 0.067445 0.056100 0.063240 44.677353

View File

@ -1 +0,0 @@
55 0.040299 0.095144 0.065489 0.078165 53.380753

View File

@ -1 +0,0 @@
55 0.040405 0.097322 0.066765 0.079255 53.374428

View File

@ -1 +0,0 @@
55 0.038764 0.090667 0.063157 0.076659 53.355133

View File

@ -1 +0,0 @@
53 0.038673 0.089891 0.063106 0.076448 53.004646

View File

@ -1 +0,0 @@
53 0.038899 0.091212 0.063614 0.076383 52.351440

View File

@ -1 +0,0 @@
54 0.038586 0.088787 0.063747 0.075643 53.104782

View File

@ -1 +0,0 @@
53 0.037872 0.087844 0.061520 0.075063 52.219879

View File

@ -1 +0,0 @@
50 0.037195 0.086863 0.060343 0.073201 50.691704

View File

@ -1 +0,0 @@
53 0.038759 0.092685 0.063432 0.076361 52.002579

View File

@ -1 +0,0 @@
52 0.040353 0.090205 0.065287 0.077373 51.746628

View File

@ -1 +0,0 @@
52 0.040583 0.091694 0.066280 0.077802 51.581718

View File

@ -1 +0,0 @@
49 0.041297 0.094172 0.067527 0.076987 50.565460

View File

@ -1 +0,0 @@
50 0.040312 0.090680 0.065473 0.075966 50.742157

View File

@ -1 +0,0 @@
50 0.039734 0.092913 0.064935 0.076198 50.960922

View File

@ -1 +0,0 @@
48 0.040137 0.089994 0.065064 0.074114 49.276997

View File

@ -1 +0,0 @@
47 0.040732 0.094854 0.067243 0.076296 49.356400

View File

@ -1 +0,0 @@
54 0.038630 0.087661 0.063683 0.075626 52.907776

View File

@ -1 +0,0 @@
48 0.041461 0.093046 0.068027 0.076524 49.280266

View File

@ -1 +0,0 @@
46 0.040502 0.082802 0.064679 0.074476 48.285706

View File

@ -1 +0,0 @@
48 0.040258 0.083422 0.064046 0.075326 49.381264

View File

@ -1 +0,0 @@
48 0.040601 0.090676 0.066223 0.076538 49.821045

View File

@ -1 +0,0 @@
46 0.039033 0.083210 0.063061 0.073626 48.440258

View File

@ -1 +0,0 @@
43 0.032891 0.045700 0.029322 0.035714 35.217300

View File

@ -1 +0,0 @@
50 0.041740 0.059307 0.038156 0.048756 40.737473

View File

@ -1 +0,0 @@
51 0.043773 0.062391 0.039251 0.053317 44.349907

View File

@ -1 +0,0 @@
50 0.044851 0.059177 0.037444 0.047610 41.626480

View File

@ -1 +0,0 @@
49 0.043650 0.054320 0.035061 0.041959 39.615734

View File

@ -1 +0,0 @@
55 0.039047 0.089085 0.064675 0.076483 53.375942

Some files were not shown because too many files have changed in this diff Show More