porn-vault: init at 0.30.0-rc.11 (#355785)

This commit is contained in:
Thiago Kenji Okada 2024-11-20 21:52:34 +00:00 committed by GitHub
commit 778f30c08c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 2790 additions and 0 deletions

View File

@ -13078,6 +13078,13 @@
githubId = 30698906;
name = "Luna D Dragon";
};
luNeder = {
email = "luana@luana.dev.br";
matrix = "@luana:catgirl.cloud";
github = "LuNeder";
githubId = 19750714;
name = "Luana Neder";
};
lunik1 = {
email = "ch.nixpkgs@themaw.xyz";
matrix = "@lunik1:lunik.one";

View File

@ -1505,6 +1505,7 @@
./services/web-apps/pingvin-share.nix
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/porn-vault/default.nix
./services/web-apps/powerdns-admin.nix
./services/web-apps/pretalx.nix
./services/web-apps/pretix.nix

View File

@ -0,0 +1,158 @@
# See https://gitlab.com/porn-vault/porn-vault/-/blob/dev/config.example.json
{
auth = {
password = null;
};
binaries = {
ffmpeg = "ffmpeg";
ffprobe = "ffprobe";
izzyPort = 8000;
imagemagick = {
convertPath = "convert";
montagePath = "montage";
identifyPath = "identify";
};
};
import = {
images = [
{
path = "/media/porn-vault/images";
include = [ ];
exclude = [ ];
extensions = [
".jpg"
".jpeg"
".png"
".gif"
];
enable = true;
}
];
videos = [
{
path = "/media/porn-vault/videos";
include = [ ];
exclude = [ ];
extensions = [
".mp4"
".mov"
".webm"
];
enable = true;
}
];
scanInterval = 10800000;
};
log = {
level = "debug";
maxSize = "20m";
maxFiles = "5";
writeFile = [
{
level = "debug";
prefix = "errors-";
silent = false;
}
];
};
matching = {
applyActorLabels = [
"event:actor:create"
"event:actor:find-unmatched-scenes"
"plugin:actor:create"
"event:scene:create"
"plugin:scene:create"
"event:image:create"
"plugin:marker:create"
"event:marker:create"
];
applySceneLabels = true;
applyStudioLabels = [
"event:studio:create"
"event:studio:find-unmatched-scenes"
"plugin:studio:create"
"event:scene:create"
"plugin:scene:create"
];
extractSceneActorsFromFilepath = true;
extractSceneLabelsFromFilepath = true;
extractSceneMoviesFromFilepath = true;
extractSceneStudiosFromFilepath = true;
matcher = {
type = "word";
options = {
ignoreSingleNames = false;
ignoreDiacritics = true;
enableWordGroups = true;
wordSeparatorFallback = true;
camelCaseWordGroups = true;
overlappingMatchPreference = "longest";
groupSeparators = [
"[\\s',()[\\]{}*\\.]"
];
wordSeparators = [
"[-_]"
];
filepathSeparators = [
"[/\\\\&]"
];
};
};
matchCreatedActors = true;
matchCreatedStudios = true;
matchCreatedLabels = true;
};
persistence = {
backup = {
enable = true;
maxAmount = 10;
};
libraryPath = "/media/porn-vault/lib";
};
plugins = {
allowActorThumbnailOverwrite = false;
allowMovieThumbnailOverwrite = false;
allowSceneThumbnailOverwrite = false;
allowStudioThumbnailOverwrite = false;
createMissingActors = false;
createMissingLabels = false;
createMissingMovies = false;
createMissingStudios = false;
events = {
actorCreated = [ ];
actorCustom = [ ];
sceneCreated = [ ];
sceneCustom = [ ];
movieCustom = [ ];
studioCreated = [ ];
studioCustom = [ ];
};
register = { };
markerDeduplicationThreshold = 5;
};
processing = {
generatePreviews = true;
readImagesOnImport = false;
generateImageThumbnails = true;
};
server = {
https = {
certificate = "";
enable = false;
key = "";
};
};
transcode = {
hwaDriver = null;
vaapiDevice = "/dev/dri/renderD128";
h264 = {
preset = "veryfast";
crf = 23;
};
webm = {
deadline = "realtime";
cpuUsed = 3;
crf = 31;
};
};
}

View File

@ -0,0 +1,110 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.porn-vault;
configFormat = pkgs.formats.json { };
defaultConfig = import ./default-config.nix;
inherit (lib)
mkIf
mkEnableOption
mkPackageOption
mkOption
getExe
literalExpression
types
;
in
{
options = {
services.porn-vault = {
enable = lib.mkEnableOption "Porn-Vault";
package = lib.mkPackageOption pkgs "porn-vault" { };
autoStart = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to start porn-vault automatically.
'';
};
port = lib.mkOption {
type = lib.types.port;
default = 3000;
description = ''
Which port Porn-Vault will use.
'';
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to open the Porn-Vault port in the firewall.
'';
};
settings = mkOption {
type = configFormat.type;
description = ''
Configuration for Porn-Vault. The attributes are serialized to JSON in config.json.
See https://gitlab.com/porn-vault/porn-vault/-/blob/dev/config.example.json
'';
default = defaultConfig;
apply = lib.recursiveUpdate defaultConfig;
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.services.porn-vault = {
description = "Porn-Vault server";
environment = {
PV_CONFIG_FOLDER = "/etc/porn-vault";
NODE_ENV = "production";
DATABASE_NAME = "production";
PORT = toString cfg.port;
};
serviceConfig = {
ExecStart = getExe cfg.package;
CacheDirectory = "porn-vault";
# Hardening options
CapabilityBoundingSet = [ "CAP_SYS_NICE" ];
AmbientCapabilities = [ "CAP_SYS_NICE" ];
LockPersonality = true;
NoNewPrivileges = true;
PrivateTmp = true;
ProtectControlGroups = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = true;
RestrictNamespaces = true;
RestrictSUIDSGID = true;
Restart = "on-failure";
RestartSec = 5;
};
wantedBy = mkIf cfg.autoStart [ "multi-user.target" ];
wants = [ "network.target" ];
};
environment.etc = {
"porn-vault/config.json".source = configFormat.generate "config.json" cfg.settings;
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
};
meta.maintainers = [ lib.maintainers.luNeder ];
}

2291
pkgs/by-name/po/porn-vault/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
diff --git a/server/binaries/ffmpeg.ts b/server/binaries/ffmpeg.ts
index abb4de4f..cdcc0a02 100644
--- a/server/binaries/ffmpeg.ts
+++ b/server/binaries/ffmpeg.ts
@@ -8,6 +8,7 @@ import { getConfig } from "../config";
import { mkdirpAsync, rimrafAsync, statAsync } from "../utils/fs/async";
import { formatMessage, handleError, logger } from "../utils/logger";
import { generateTimestampsAtIntervals } from "../utils/misc";
+import { tempPath } from "server/utils/path";
export async function takeScreenshot(
inPath: string,
@@ -112,7 +113,7 @@ export async function generatePreview(
): Promise<void> {
logger.debug(`Creating 100 small previews for ${sceneId}.`);
- const tmpFolder = resolve("tmp", "preview", sceneId);
+ const tmpFolder = resolve(tempPath, "preview", sceneId);
const timestamps = generateTimestampsAtIntervals(PREVIEW_COUNT, durationSecs * 1000, {
startPercentage: 2,
diff --git a/server/database/index.ts b/server/database/index.ts
index 80ff6432..c6feb11d 100755
--- a/server/database/index.ts
+++ b/server/database/index.ts
@@ -15,7 +15,7 @@ import Studio from "../types/studio";
import SceneView from "../types/watch";
import { mkdirpSync } from "../utils/fs/async";
import { logger } from "../utils/logger";
-import { libraryPath } from "../utils/path";
+import { libraryPath, tempPath } from "../utils/path";
import { Izzy } from "./internal";
export function formatCollectionName(name: string) {
@@ -261,11 +261,11 @@ export async function loadStore<T extends { _id: string }>(
}
export async function loadStores(): Promise<void> {
- if (!existsSync("tmp")) {
- logger.info("Creating temporary directory 'tmp'");
- mkdirpSync("tmp/");
+ if (!existsSync(tempPath)) {
+ logger.info(`Creating temporary directory '${tempPath}'`);
+ mkdirpSync(tempPath);
} else {
- logger.debug("Temporary directory 'tmp' already exists");
+ logger.debug(`Temporary directory '${tempPath}' already exists`);
}
const crossReferencePath = libraryPath("cross_references.db");
diff --git a/server/graphql/mutations/image.ts b/server/graphql/mutations/image.ts
index 6554f145..a7853eea 100644
--- a/server/graphql/mutations/image.ts
+++ b/server/graphql/mutations/image.ts
@@ -20,7 +20,7 @@ import Studio from "../../types/studio";
import { mapAsync } from "../../utils/async";
import { copyFileAsync, statAsync, unlinkAsync } from "../../utils/fs/async";
import { logger } from "../../utils/logger";
-import { getFolderPartition, libraryPath } from "../../utils/path";
+import { getFolderPartition, libraryPath, tempPath } from "../../utils/path";
import { getExtension, normalizeName } from "../../utils/string";
import { Dictionary, isBoolean, isNumber, isString } from "../../utils/types";
import { clearCaches } from "../datasources";
@@ -110,7 +110,7 @@ export default {
const image = new Image(imageName);
- const outPath = `tmp/${image._id}${ext}`;
+ const outPath = resolve(tempPath, `${image._id}${ext}`);
logger.debug(`Getting file...`);
diff --git a/server/routes/scene.ts b/server/routes/scene.ts
index 601de160..fe8b8de5 100644
--- a/server/routes/scene.ts
+++ b/server/routes/scene.ts
@@ -16,7 +16,7 @@ import Scene from "../types/scene";
import { mkdirpAsync, readFileAsync, rimrafAsync } from "../utils/fs/async";
import { handleError, logger } from "../utils/logger";
import { generateTimestampsAtIntervals } from "../utils/misc";
-import { getFolderPartition, libraryPath } from "../utils/path";
+import { getFolderPartition, libraryPath, tempPath } from "../utils/path";
import { IMAGE_CACHE_CONTROL } from "./media";
/* function streamTranscode(
@@ -94,7 +94,7 @@ export async function attachScenePreviewGrid(scene: Scene): Promise<string | nul
return null;
}
- const gridFolder = path.resolve("tmp", "grid");
+ const gridFolder = path.resolve(tempPath, "grid");
const tmpFolder = path.resolve(gridFolder, "thumbs", randomUUID());
await mkdirpAsync(tmpFolder);
diff --git a/server/utils/path.ts b/server/utils/path.ts
index 05619e93..64964de8 100644
--- a/server/utils/path.ts
+++ b/server/utils/path.ts
@@ -5,6 +5,7 @@ import { getConfig } from "../config";
import { mkdirpSync } from "./fs/async";
const configFolder = process.env.PV_CONFIG_FOLDER || process.cwd();
+export const tempPath = process.env.CACHE_DIRECTORY ?? "tmp";
export function libraryPath(str: string): string {
return resolve(getConfig().persistence.libraryPath, "library", str);
--
2.47.0

View File

@ -0,0 +1,114 @@
{
fetchFromGitLab,
fetchurl,
rustPlatform,
lib,
pnpm_9,
stdenvNoCC,
nodejs_22,
ffmpeg,
imagemagick,
makeWrapper,
autoPatchelfHook,
writeShellApplication,
}:
let
izzy = rustPlatform.buildRustPackage rec {
pname = "izzy";
version = "2.0.1";
src = fetchFromGitLab {
owner = "porn-vault";
repo = "izzy";
rev = version;
hash = "sha256-UauA5mZi5a5QF7d17pKSzvyaWbeSuFjBrXEAxR3wNkk=";
};
postPatch = ''
ln -s ${./Cargo.lock} Cargo.lock
'';
cargoLock.lockFile = ./Cargo.lock;
meta = {
description = "Rust In-Memory K-V Store with Redis-Style File Persistence and Secondary Indices";
homepage = "https://gitlab.com/porn-vault/izzy";
license = lib.licenses.gpl3Plus;
maintainers = [ lib.maintainers.luNeder ];
mainProgram = "izzy";
};
};
pnpm = pnpm_9;
nodejs = nodejs_22;
in
stdenvNoCC.mkDerivation (finalAttrs: {
pname = "porn-vault";
version = "0.30.0-rc.11";
src = fetchFromGitLab {
owner = "porn-vault";
repo = "porn-vault";
rev = "4c6182c5825d85193cf67cb7cd927da2feaaecdb";
hash = "sha256-wQ3dqLc0l2BmLGDYrbWxX2mPwO/Tqz0fY/fOQTEUv24=";
};
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "sha256-Xr9tRiP1hW+aFs9FnPvPkeJ0/LtJI57cjWY5bZQaRTQ=";
};
nativeBuildInputs = [
nodejs
pnpm.configHook
makeWrapper
];
patches = [
./allow-use-of-systemd-temp-path.patch
];
postPatch = ''
substituteInPlace server/binaries/izzy.ts \
--replace-fail 'chmodSync(izzyPath, "111");' ""
'';
buildPhase = ''
runHook preBuild
pnpm build
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -Dm644 package.json config.example.json remix.config.js -t $out/share/porn-vault
cp -R public dist build node_modules graphql locale -t $out/share/porn-vault
runHook postInstall
'';
preFixup = ''
makeWrapper "${lib.getExe nodejs}" "$out/bin/porn-vault" \
--chdir "$out/share/porn-vault" \
--add-flags "dist/index.js" \
--set-default IZZY_PATH "${lib.getExe izzy}" \
--prefix PATH : "${
lib.makeBinPath [
ffmpeg
imagemagick
izzy
]
}"
'';
meta = {
description = "Porn-Vault is a self hosted organizer for adult videos and imagery.";
homepage = "https://gitlab.com/porn-vault/porn-vault";
license = lib.licenses.gpl3Plus;
maintainers = [ lib.maintainers.luNeder ];
inherit (nodejs.meta) platforms;
mainProgram = "porn-vault";
};
})