init f0ckm

This commit is contained in:
2026-04-25 19:51:52 +02:00
commit b646107eb7
241 changed files with 70364 additions and 0 deletions

23
scripts/build-css.mjs Normal file
View File

@@ -0,0 +1,23 @@
import fs from 'fs';
import path from 'path';
const cssDir = path.join(process.cwd(), 'public/s/css');
const files = ['f0ckm.css', 'v0ck.css'];
const outputFile = path.join(cssDir, 'bundle.css');
let combined = '';
files.forEach(file => {
const content = fs.readFileSync(path.join(cssDir, file), 'utf8');
combined += content + '\n';
});
// Simple minification
const minified = combined
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove comments
.replace(/\s*([\{\}:;,])\s*/g, '$1') // Remove whitespace around selectors/properties
.replace(/\s+/g, ' ') // Collapse multiple whitespaces
.trim();
fs.writeFileSync(outputFile, minified);
console.log(`Bundle created: ${outputFile} (${combined.length} -> ${minified.length} bytes)`);

View File

@@ -0,0 +1,63 @@
import db from "../src/inc/sql.mjs";
import cfg from "../src/inc/config.mjs";
import fs from "fs";
import path from "path";
async function run() {
console.log("[BOOT] Starting coverart cleanup and migration...");
// 1. Get all audio items
const audioItems = await db`SELECT id, dest FROM items WHERE mime LIKE 'audio/%' AND is_deleted = false`;
console.log(`[INFO] Found ${audioItems.length} audio items to evaluate.`);
const PLACEHOLDER_SIZE = 649524; // Exact size of music.webp
const OLD_PLACEHOLDER_MAX = 5000; // Max size of old gray placeholder
let uniqueCount = 0;
let placeholderCount = 0;
let missingCount = 0;
let count = 0;
for (const item of audioItems) {
process.stdout.write(`\r[${++count}/${audioItems.length}] Evaluating ${item.id}...`);
const caPath = path.join(cfg.paths.ca, `${item.id}.webp`);
let hasUniqueArt = false;
if (fs.existsSync(caPath)) {
const stats = fs.statSync(caPath);
if (stats.size === PLACEHOLDER_SIZE || stats.size < OLD_PLACEHOLDER_MAX) {
// It's a placeholder (new or old)
// Delete it and set flag to false
try {
fs.unlinkSync(caPath);
placeholderCount++;
} catch (err) {
console.error(`\n[ERROR] Failed to delete ${item.id} placeholder:`, err.message);
}
} else {
// It's likely a unique extracted cover
hasUniqueArt = true;
uniqueCount++;
}
} else {
missingCount++;
}
// Update database
await db`UPDATE items SET has_coverart = ${hasUniqueArt} WHERE id = ${item.id}`;
}
console.log("\n[FINISH] Migration Complete.");
console.log(`[STATS] Total: ${audioItems.length}`);
console.log(`[STATS] Unique Covers Kept: ${uniqueCount}`);
console.log(`[STATS] Placeholders Deleted: ${placeholderCount}`);
console.log(`[STATS] Missing/Already Clean: ${missingCount}`);
process.exit(0);
}
run().catch(err => {
console.error("[FATAL]", err);
process.exit(1);
});

View File

@@ -0,0 +1,53 @@
import db from "../src/inc/sql.mjs";
import cfg from "../src/inc/config.mjs";
import queue from "../src/inc/queue.mjs";
import fs from "fs";
import path from "path";
async function run() {
console.log("[BOOT] Starting coverart recreation script...");
const audioItems = await db`SELECT id, dest, mime FROM items WHERE mime LIKE 'audio/%' AND is_deleted = false`;
console.log(`[INFO] Found ${audioItems.length} audio items.`);
let count = 0;
for (const item of audioItems) {
process.stdout.write(`\r[${++count}/${audioItems.length}] Processing ${item.id}...`);
try {
const caPath = path.join(cfg.paths.ca, `${item.id}.webp`);
const tPath = path.join(cfg.paths.t, `${item.id}.webp`);
let needsProcessing = !fs.existsSync(caPath);
if (!needsProcessing) {
const stats = fs.statSync(caPath);
if (stats.size < 5000) needsProcessing = true; // Detect old gray placeholder
}
if (needsProcessing) {
try {
// Try extraction first
await queue.genThumbnail(item.dest, item.mime, item.id, '', false);
} catch (e) {
// If extraction fails, use the music.webp fallback
await fs.promises.copyFile('public/s/img/music.webp', caPath);
}
}
if (!fs.existsSync(tPath)) {
try {
await queue.spawn('magick', ['-size', '128x128', 'xc:#1a1a1a', tPath]);
} catch (err) { }
}
} catch (e) {
console.error(`\n[ERROR] ${item.id} processing failed:`, e.message);
}
}
console.log("\n[FINISH] All audio items processed.");
process.exit(0);
}
run().catch(err => {
console.error("[FATAL]", err);
process.exit(1);
});

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env node
/**
* Regenerate thumbnails and coverart for specific items.
*
* Usage:
* node regen.mjs <itemid> - Regenerate a single item
* node regen.mjs <id1> <id2> ... - Regenerate multiple items
* node regen.mjs --all - Regenerate ALL items
* node regen.mjs --audio - Regenerate all audio items
*/
import db from "./src/inc/sql.mjs";
import queue from "./src/inc/queue.mjs";
import cfg from "./src/inc/config.mjs";
import fs from "fs/promises";
import path from "path";
const args = process.argv.slice(2);
if (args.length === 0) {
console.log('Usage:');
console.log(' node regen.mjs <itemid> - Regenerate a single item');
console.log(' node regen.mjs <id1> <id2> ... - Regenerate multiple items');
console.log(' node regen.mjs --all - Regenerate ALL items');
console.log(' node regen.mjs --audio - Regenerate all audio items');
process.exit(0);
}
const regen = async (item) => {
const { id, dest, mime, src } = item;
console.log(`[${id}] Regenerating: ${dest} (${mime})`);
try {
await queue.genThumbnail(dest, mime, id, src || '', false);
if (mime.startsWith('audio/') && queue._lastCoverExtracted) {
await db`UPDATE items SET has_coverart = TRUE WHERE id = ${id}`;
console.log(`[${id}] ✓ Cover art extracted and saved`);
} else if (mime.startsWith('audio/')) {
await db`UPDATE items SET has_coverart = FALSE WHERE id = ${id}`;
console.log(`[${id}] ✓ No cover art found, placeholder generated`);
} else {
console.log(`[${id}] ✓ Thumbnail regenerated`);
}
// Regenerate blurred thumbnail if item has NSFW tag
const nsfw = await db`SELECT 1 FROM tags_assign WHERE item_id = ${id} AND tag_id = 2 LIMIT 1`;
if (nsfw.length > 0) {
await queue.genBlurredThumbnail(id, false);
console.log(`[${id}] ✓ Blurred thumbnail regenerated`);
}
} catch (err) {
console.error(`[${id}] ✗ FAILED:`, err.message || err);
}
};
try {
let items;
if (args.includes('--all')) {
items = await db`SELECT id, dest, mime, src FROM items WHERE active = true AND is_deleted = false ORDER BY id`;
console.log(`Regenerating ALL ${items.length} items...\n`);
} else if (args.includes('--audio')) {
items = await db`SELECT id, dest, mime, src FROM items WHERE active = true AND is_deleted = false AND mime ILIKE 'audio/%' ORDER BY id`;
console.log(`Regenerating ${items.length} audio items...\n`);
} else {
const ids = args.map(Number).filter(n => !isNaN(n) && n > 0);
if (ids.length === 0) {
console.error('No valid item IDs provided.');
process.exit(1);
}
items = await db`SELECT id, dest, mime, src FROM items WHERE id IN ${db(ids)} ORDER BY id`;
const found = items.map(i => i.id);
const missing = ids.filter(id => !found.includes(id));
if (missing.length) console.warn(`Items not found: ${missing.join(', ')}\n`);
}
for (const item of items) {
await regen(item);
}
console.log(`\nDone. ${items.length} items processed.`);
process.exit(0);
} catch (err) {
console.error('Fatal error:', err);
process.exit(1);
}