use buttplug svg
Some checks failed
ci / build (push) Has been cancelled
ci / test (push) Has been cancelled

This commit is contained in:
CJ_Clippy 2025-08-29 21:52:35 -08:00
parent 3ffbcbab8e
commit e0e10fd926
278 changed files with 2022 additions and 309303 deletions

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
lts/jod

View File

@ -3,6 +3,7 @@
echo(" * Remux the .ts to .mp4 using `ffmpeg -i 2025-08-25T00-12-24Z.ts -c copy projektmelody-chaturbate-2025-08-24.mp4")
echo(" If multiple files, use `ffmpeg -f concat -safe 0 -i ./files.txt -c copy ./projektmelody-chaturbate-2025-08-24.mp4`")
echo(" [Press Enter When Complete...]")
_ = stdin()

View File

@ -57,7 +57,7 @@ RUN npm run build
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
# Install torrentfile & other python deps
# Install python deps
RUN ./venv/bin/pip install --no-cache-dir -r requirements.txt
# Expose the port

30
services/our/build.mjs Normal file
View File

@ -0,0 +1,30 @@
// build.mjs
import esbuild from "esbuild";
import copyStaticFiles from "esbuild-copy-static-files";
const build = async () => {
console.log("🏗️ Building...");
await esbuild.build({
entryPoints: ["./src/client/index.js", './src/client/vod.js'],
outdir: "./dist/client",
bundle: true,
minify: true,
sourcemap: true,
platform: "browser",
target: "es2020",
logLevel: "info",
color: true,
plugins: [
copyStaticFiles({
src: "./src/assets",
dest: "./dist/client",
recursive: true,
})
],
});
};
build().catch((err) => {
console.error(err);
process.exit(1);
});

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,13 @@
"version": "2.5.0",
"type": "module",
"scripts": {
"dev": "concurrently npm:dev:serve npm:dev:build npm:dev:worker npm:dev:compose npm:dev:sftp",
"dev": "concurrently npm:dev:serve npm:dev:build:server npm:dev:build:client npm:dev:worker npm:dev:compose npm:dev:sftp",
"dev:serve": "npx @dotenvx/dotenvx run -f ../../.env.development.local -- tsx watch ./src/index.ts",
"dev:compose": "docker compose -f compose.development.yaml up",
"dev:worker": "npx @dotenvx/dotenvx run -e GRAPHILE_LOGGER_DEBUG=1 -f ../../.env.development.local -- tsx watch ./src/worker.ts",
"dev:build": "chokidar 'src/**/*.{js,ts}' -c tsup --clean",
"dev:build": "echo please use either dev:build:server or dev:build:client",
"dev:build:server": "chokidar 'src/**/*.{js,ts}' --ignore 'src/client/**' -c tsup --clean",
"dev:build:client": "chokidar 'src/client/**/*.{js,css}' -c 'node build.mjs'",
"dev:sftp": "docker run -p 2222:22 --rm atmoz/sftp user:pass:::watch",
"start": "echo please use either start:server or start:worker; exit 1",
"start:server": "tsx ./src/index.ts",
@ -24,6 +26,9 @@
"devDependencies": {
"@eslint/compat": "^1.3.1",
"@eslint/js": "^9.31.0",
"chokidar": "^4.0.3",
"esbuild": "^0.25.9",
"esbuild-copy-static-files": "^0.1.0",
"eslint": "^9.31.0",
"eslint-plugin-svelte": "^3.10.1",
"globals": "^16.3.0",
@ -31,7 +36,11 @@
"prisma": "6.8.2",
"typescript": "^5.8.3",
"typescript-eslint": "^8.36.0",
"vitest": "^3.2.4"
"video.js": "^8.23.4",
"videojs-contrib-quality-levels": "^4.1.0",
"videojs-hls-quality-selector": "^2.0.0",
"vitest": "^3.2.4",
"yargs": "^18.0.0"
},
"pnpm": {
"onlyBuiltDependencies": [
@ -58,6 +67,7 @@
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "6.8.2",
"@prisma/extension-accelerate": "^1.3.0",
"@types/fs-extra": "^11.0.4",
"@types/node": "^22.16.3",
"@types/node-fetch": "^2.6.12",
"cache-manager": "^7.0.1",
@ -67,10 +77,11 @@
"create-torrent": "^6.1.0",
"date-fns": "^4.1.0",
"fastify": "^5.4.0",
"fastify-favicon": "^5.0.0",
"fastify-plugin": "^5.0.1",
"fastify-sse-v2": "^4.2.1",
"form-data": "^4.0.3",
"fs-extra": "^11.3.0",
"fs-extra": "^11.3.1",
"graphile-worker": "^0.16.6",
"handlebars": "4.7.8",
"jdenticon": "^3.3.0",
@ -84,8 +95,6 @@
"onnxruntime-node": "1.22.0-rev",
"pino": "^9.7.0",
"pino-pretty": "^13.0.0",
"pnpm": "^10.14.0",
"protobufjs": "^7.5.3",
"rate-limiter-flexible": "^7.1.1",
"rimraf": "6.0.1",
"sharp": "^0.34.3",

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Vod" ADD COLUMN "sourceVideoDuration" INTEGER;

View File

@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "Vod" ADD COLUMN "sourceAudioCodec" TEXT,
ADD COLUMN "sourceVideoCodec" TEXT,
ADD COLUMN "sourceVideoFps" DOUBLE PRECISION;

View File

@ -0,0 +1,10 @@
/*
Warnings:
- You are about to drop the column `funscript` on the `Vod` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Vod" DROP COLUMN "funscript",
ADD COLUMN "funscriptThrust" TEXT,
ADD COLUMN "funscriptVibrate" TEXT;

View File

@ -71,21 +71,26 @@ model Vod {
uploaderId String // previously in Upload
uploader User @relation(fields: [uploaderId], references: [id])
streamDate DateTime
notes String?
segmentKeys Json?
sourceVideo String?
hlsPlaylist String?
thumbnail String?
asrVttKey String?
slvttSheetKeys Json?
slvttVTTKey String?
magnetLink String?
streamDate DateTime
notes String?
segmentKeys Json?
sourceVideo String?
sourceVideoDuration Int? // milliseconds
sourceVideoCodec String?
sourceAudioCodec String?
sourceVideoFps Float?
hlsPlaylist String?
thumbnail String?
asrVttKey String?
slvttSheetKeys Json?
slvttVTTKey String?
magnetLink String?
status VodStatus @default(pending)
sha256sum String?
cidv1 String?
funscript String?
status VodStatus @default(pending)
sha256sum String?
cidv1 String?
funscriptVibrate String?
funscriptThrust String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

View File

@ -10,7 +10,7 @@ import adminRoutes from './plugins/admin'
import hls from './plugins/hls.ts'
import fastifyStatic from '@fastify/static'
import fastifySecureSession from '@fastify/secure-session'
import path, { basename } from 'node:path'
import path, { basename, resolve } from 'node:path'
import fastifyFormbody from '@fastify/formbody'
import fastifyView from "@fastify/view"
import { env } from './config/env'
@ -21,102 +21,19 @@ import graphileWorker from './plugins/graphileWorker'
import fastifySwagger from "@fastify/swagger"
import fastifySwaggerUi from "@fastify/swagger-ui"
import { join } from 'node:path'
import { format } from 'date-fns'
import * as jdenticon from 'jdenticon'
import { Role } from '../generated/prisma'
import fastifyFlash from '@fastify/flash'
import { isModerator, hasRole } from './utils/privs'
import { signUrl } from './utils/cdn'
import { extractBasePath } from './utils/filesystem'
import { truncate } from './utils/formatters.ts'
import { icons } from './utils/icons.ts'
import logger from './utils/logger.ts'
import fastifyCaching from '@fastify/caching'
import { registerHbsHelpers } from './utils/hbsHelpers.ts'
import fastifyFavicon from 'fastify-favicon'
export function buildApp() {
const __dirname = import.meta.dirname;
export async function buildApp() {
const app = Fastify()
Handlebars.registerHelper('formatDate', function (dateString) {
if (!dateString) return ''
return format(new Date(dateString), 'yyyy-MM-dd')
})
Handlebars.registerHelper('identicon', function (str, size = 48) {
return jdenticon.toSvg(str, size)
})
Handlebars.registerHelper('safeJson', function (context) {
return new Handlebars.SafeString(JSON.stringify(context));
});
Handlebars.registerHelper('json', function (context) {
return JSON.stringify(context)
})
Handlebars.registerHelper('patron', function (user) {
if (!user.roles) {
throw new Error(
'patron hbs helper was called without roles. This usually means you forgot to include roles relationship in the query.'
);
}
return user.roles.some((r: Role) => r.name.startsWith('supporter'));
});
Handlebars.registerHelper('notEqual', function (a, b) {
return a !== b;
});
Handlebars.registerHelper('isEqual', function (a, b) {
logger.trace(`isEqual a=${a} b=${b}`)
return a == b
});
Handlebars.registerHelper('isModerator', function (user) {
return isModerator(user)
})
Handlebars.registerHelper('hasRole', hasRole)
Handlebars.registerHelper('breaklines', function (text) {
text = Handlebars.Utils.escapeExpression(text);
text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
return new Handlebars.SafeString(text);
});
Handlebars.registerHelper('getCdnUrl', function (s3Key) {
// Before you remove this log, find a way to memoize this function!
logger.info(`getCdnUrl called with CDN_ORIGIN=${env.CDN_ORIGIN} and CDN_TOKEN_SECRET=${env.CDN_TOKEN_SECRET}`)
return signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
securityKey: env.CDN_TOKEN_SECRET,
expirationTime: constants.timeUnits.sevenDaysInSeconds,
})
})
/**
* @see https://github.com/video-dev/hls.js/issues/2152
*/
Handlebars.registerHelper('signedHlsUrl', function (s3Key) {
if (!s3Key) throw new Error(`signedHlsUrl called with falsy s3Key=${s3Key}`);
const pathAllowed = extractBasePath(s3Key)
const url = signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
securityKey: env.CDN_TOKEN_SECRET,
pathAllowed,
isDirectory: true,
expirationTime: constants.timeUnits.sevenDaysInSeconds,
})
logger.debug(`pathAllowed=${pathAllowed} url=${url}`)
return url
})
Handlebars.registerHelper('basename', function (url: string) {
return basename(url)
})
Handlebars.registerHelper('trunc', function (str, length = 6) {
return truncate(str, length)
});
Handlebars.registerHelper('icon', function (name: string, size = 20) {
const svg = icons[name];
registerHbsHelpers(Handlebars)
if (!svg) {
return new Handlebars.SafeString(`<!-- icon "${name}" not found -->`);
}
// Inject width/height if not already set
const sizedSvg = svg
.replace(/<svg([^>]*)>/, `<svg$1 width="${size}" height="${size}">`);
return new Handlebars.SafeString(sizedSvg);
});
const __dirname = import.meta.dirname;
const swaggerOptions = {
swagger: {
info: {
@ -136,12 +53,18 @@ export function buildApp() {
exposeRoute: true,
};
app.register(fastifySwagger, swaggerOptions);
app.register(fastifySwaggerUi, swaggerUiOptions);
app.register(fastifyFavicon, { path: path.join(__dirname, '..', 'dist', 'client'), name: 'favicon.ico', maxAge: 3600 })
app.register(fastifyStatic, {
root: path.join(__dirname, 'assets'),
prefix: '/', // optional: default '/'
root: path.join(__dirname, '..', 'dist', 'client'),
prefix: '/assets', // optional: default '/'
constraints: {} // optional: default {}
})
app.register(fastifyFormbody)
@ -192,6 +115,8 @@ export function buildApp() {
app.register(hls)
app.register(vodsRoutes)
app.register(streamsRoutes)
app.register(vtubersRoutes)
app.register(uploadsRoutes)
@ -200,5 +125,6 @@ export function buildApp() {
app.register(adminRoutes)
app.register(authRoutes)
return app
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

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