From 381daeeac9b842548188025f6b612c25fb84c018 Mon Sep 17 00:00:00 2001 From: CJ_Clippy Date: Sat, 19 Jul 2025 08:13:54 -0800 Subject: [PATCH] bundle pytorch model --- services/our/.dockerignore | 2 + services/our/Dockerfile | 3 + services/our/src/config/env.ts | 2 +- services/our/src/tasks/createFunscript.ts | 240 +--------------------- 4 files changed, 8 insertions(+), 239 deletions(-) diff --git a/services/our/.dockerignore b/services/our/.dockerignore index d007df5..358265c 100644 --- a/services/our/.dockerignore +++ b/services/our/.dockerignore @@ -1,3 +1,5 @@ +!dist/vibeui.pt + runs requirements.txt venv diff --git a/services/our/Dockerfile b/services/our/Dockerfile index 1d91804..25872c4 100644 --- a/services/our/Dockerfile +++ b/services/our/Dockerfile @@ -19,6 +19,9 @@ RUN apt-get update -y && \ # Install IPFS Kubo COPY --from=ipfs/kubo:v0.36.0 /usr/local/bin/ipfs /usr/local/bin/ipfs +# Include the vibeui pytorch model +COPY ./dist/vibeui.pt /app/dist/vibeui.pt + # Copy and install dependencies COPY package.json package-lock.json ./ RUN npm install --ignore-scripts=false --foreground-scripts --verbose diff --git a/services/our/src/config/env.ts b/services/our/src/config/env.ts index 8b803a8..8ede7f6 100644 --- a/services/our/src/config/env.ts +++ b/services/our/src/config/env.ts @@ -8,7 +8,7 @@ dotenvx.config({ path: ['../../.env.development'] }) const EnvSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'test']), - VENV: z.string(), + VENV: z.string().default('/app/venv'), PORT: z.coerce.number().default(3000), DATABASE_URL: z.string().url(), ORIGIN: z.string(), diff --git a/services/our/src/tasks/createFunscript.ts b/services/our/src/tasks/createFunscript.ts index ccaa5fe..e1c9618 100644 --- a/services/our/src/tasks/createFunscript.ts +++ b/services/our/src/tasks/createFunscript.ts @@ -4,51 +4,15 @@ import { withAccelerate } from "@prisma/extension-accelerate"; import { getOrDownloadAsset } from "../utils/cache"; import { env } from "../config/env"; import { getS3Client, uploadFile } from "../utils/s3"; -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 { inference } from "../utils/vibeui"; import { preparePython } from "../utils/python"; -import { generateActions2, buildFunscript } from "../utils/funscripts"; +import { 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; -// } - -// interface FunscriptAction { -// at: number; -// pos: number; -// } - -// interface Funscript { -// version: string; -// actions: FunscriptAction[]; -// } - - -// interface ClassPositionMap { -// [className: string]: number | 'pattern'; -// } const prisma = new PrismaClient().$extends(withAccelerate()); @@ -63,206 +27,6 @@ function assertPayload(payload: any): asserts payload is Payload { -// export async function buildFunscript( -// helpers: Helpers, -// predictionOutput: string, -// videoPath: string -// ): Promise { -// 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 { -// 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 { -// const labelFiles = (await readdir(labelDir)).filter(file => file.endsWith('.txt')); -// const detections: Map = 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)[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;