diff --git a/public/s/css/f0ckm.css b/public/s/css/f0ckm.css index 3dfb355..cdbb6f4 100644 --- a/public/s/css/f0ckm.css +++ b/public/s/css/f0ckm.css @@ -14733,10 +14733,4 @@ body.scroller-active #gchat-reopen-bubble { padding: 0 !important; font-size: 11px !important; line-height: 1 !important; -} - -/* Sidebar activity thumbnails blur support (SFW/Untagged only; NSFW/NSFL use pre-blurred static images directly) */ -.blur-sfw-active .sidebar-thumb-link[data-mode="sfw"] img, -.blur-untagged-active .sidebar-thumb-link[data-mode="untagged"] img { - filter: blur(8px) contrast(0.85) brightness(0.85); } \ No newline at end of file diff --git a/public/s/js/f0ckm.js b/public/s/js/f0ckm.js index 2c86283..5fcfd1f 100644 --- a/public/s/js/f0ckm.js +++ b/public/s/js/f0ckm.js @@ -145,17 +145,24 @@ window.cancelAnimFrame = (function () { document.querySelectorAll('.lazy-thumb').forEach(thumb => { let bg = thumb.dataset.bg; if (bg) { - const mode = thumb.getAttribute('data-mode'); - const blurNsfw = localStorage.getItem('blurNsfw') === 'true'; - const blurNsfl = localStorage.getItem('blurNsfl') === 'true'; - if (mode === 'nsfw' && blurNsfw && !thumb.classList.contains('revealed')) { - bg = bg.replace('.webp', '_blur.webp'); - } else if (mode === 'nsfl' && blurNsfl && !thumb.classList.contains('revealed')) { - bg = bg.replace('.webp', '_blur.webp'); - } - const finalBg = window.applyThumbCacheBust(bg); - thumb.style.backgroundImage = `url('${finalBg}')`; - thumb.classList.remove('lazy-thumb'); + const mode = thumb.getAttribute('data-mode'); + const blurNsfw = localStorage.getItem('blurNsfw') === 'true'; + const blurNsfl = localStorage.getItem('blurNsfl') === 'true'; + const blurSfw = localStorage.getItem('blurSfw') === 'true'; + const blurUntagged = localStorage.getItem('blurUntagged') === 'true'; + + let shouldBlurThis = false; + if (mode === 'nsfw') shouldBlurThis = blurNsfw; + else if (mode === 'nsfl') shouldBlurThis = blurNsfl; + else if (mode === 'sfw') shouldBlurThis = blurSfw; + else if (mode === 'null' || !mode) shouldBlurThis = blurUntagged; + + if (shouldBlurThis && !thumb.classList.contains('revealed')) { + bg = bg.replace('.webp', '_blur.webp'); + } + const finalBg = window.applyThumbCacheBust(bg); + thumb.style.backgroundImage = `url('${finalBg}')`; + thumb.classList.remove('lazy-thumb'); } }); return; @@ -171,9 +178,16 @@ window.cancelAnimFrame = (function () { const mode = thumb.getAttribute('data-mode'); const blurNsfw = localStorage.getItem('blurNsfw') === 'true'; const blurNsfl = localStorage.getItem('blurNsfl') === 'true'; - if (mode === 'nsfw' && blurNsfw && !thumb.classList.contains('revealed')) { - bg = bg.replace('.webp', '_blur.webp'); - } else if (mode === 'nsfl' && blurNsfl && !thumb.classList.contains('revealed')) { + const blurSfw = localStorage.getItem('blurSfw') === 'true'; + const blurUntagged = localStorage.getItem('blurUntagged') === 'true'; + + let shouldBlurThis = false; + if (mode === 'nsfw') shouldBlurThis = blurNsfw; + else if (mode === 'nsfl') shouldBlurThis = blurNsfl; + else if (mode === 'sfw') shouldBlurThis = blurSfw; + else if (mode === 'null' || !mode) shouldBlurThis = blurUntagged; + + if (shouldBlurThis && !thumb.classList.contains('revealed')) { bg = bg.replace('.webp', '_blur.webp'); } @@ -8831,7 +8845,13 @@ if (navigator.vibrate) { // Put the pre-blurred background image back if applicable const baseBg = thumb.dataset.bg; const mode = thumb.getAttribute('data-mode'); - if (baseBg && (mode === 'nsfw' || mode === 'nsfl')) { + let shouldBlurThis = false; + if (mode === 'nsfw') shouldBlurThis = blurNsfw; + else if (mode === 'nsfl') shouldBlurThis = blurNsfl; + else if (mode === 'sfw') shouldBlurThis = blurSfw; + else if (mode === 'null' || !mode) shouldBlurThis = blurUntagged; + + if (baseBg && shouldBlurThis) { const finalBg = window.applyThumbCacheBust(baseBg.replace('.webp', '_blur.webp')); thumb.style.backgroundImage = `url('${finalBg}')`; } @@ -8858,7 +8878,7 @@ if (navigator.vibrate) { // Dynamically load the standard unblurred background image const baseBg = thumb.dataset.bg; - if (baseBg && (mode === 'nsfw' || mode === 'nsfl')) { + if (baseBg && shouldBlurThis) { const finalBg = window.applyThumbCacheBust(baseBg); thumb.style.backgroundImage = `url('${finalBg}')`; } diff --git a/public/s/js/sidebar-activity.js b/public/s/js/sidebar-activity.js index cdee132..498100b 100644 --- a/public/s/js/sidebar-activity.js +++ b/public/s/js/sidebar-activity.js @@ -410,7 +410,7 @@ else if (rClass === 'untagged' && blurUntagged) isBlurred = true; let thumbUrl = `/t/${c.item_id}.webp`; - if (isBlurred && (rClass === 'nsfw' || rClass === 'nsfl')) { + if (isBlurred) { thumbUrl = `/t/${c.item_id}_blur.webp`; } diff --git a/scripts/regen.mjs b/scripts/regen.mjs index d5cd1f4..bb3cf5f 100644 --- a/scripts/regen.mjs +++ b/scripts/regen.mjs @@ -8,7 +8,7 @@ * node regen.mjs --all - Regenerate ALL items * node regen.mjs --audio - Regenerate all audio items * node regen.mjs --pdf - Regenerate all PDF items - * node regen.mjs --blur - Regenerate ONLY the blurred thumbnails for NSFW/NSFL items + * node regen.mjs --blur - Regenerate ONLY the blurred thumbnails for all items */ import db from "../src/inc/sql.mjs"; @@ -27,7 +27,7 @@ if (args.length === 0) { console.log(' node regen.mjs --audio - Regenerate all audio items'); console.log(' node regen.mjs --pdf - Regenerate all PDF items'); console.log(' node regen.mjs --youtube - Regenerate all YouTube thumbnails'); - console.log(' node regen.mjs --blur - Regenerate ONLY the blurred thumbnails for NSFW/NSFL items'); + console.log(' node regen.mjs --blur - Regenerate ONLY the blurred thumbnails for all items'); process.exit(0); } @@ -64,12 +64,9 @@ const regen = async (item) => { 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`); - } + // Regenerate blurred thumbnail unconditionally + await queue.genBlurredThumbnail(id, false); + console.log(`[${id}] ✓ Blurred thumbnail regenerated`); } catch (err) { console.error(`[${id}] ✗ FAILED:`, err.message || err); } @@ -91,16 +88,13 @@ try { items = await db`SELECT id, dest, mime, src FROM items WHERE active = true AND is_deleted = false AND mime = 'video/youtube' ORDER BY id`; console.log(`Regenerating ${items.length} YouTube items...\n`); } else if (blurOnly) { - const nsflTagId = cfg.nsfl_tag_id || 3; items = await db` - SELECT DISTINCT i.id, i.dest, i.mime, i.src - FROM items i - JOIN tags_assign ta ON ta.item_id = i.id - WHERE i.active = true AND i.is_deleted = false - AND ta.tag_id IN (2, ${nsflTagId}) - ORDER BY i.id + SELECT id, dest, mime, src + FROM items + WHERE active = true AND is_deleted = false + ORDER BY id `; - console.log(`Regenerating ONLY blurred thumbnails for ${items.length} NSFW/NSFL items...\n`); + console.log(`Regenerating ONLY blurred thumbnails for all ${items.length} items...\n`); } else { const ids = args.map(Number).filter(n => !isNaN(n) && n > 0); if (ids.length === 0) { diff --git a/src/inc/routes/apiv2/index.mjs b/src/inc/routes/apiv2/index.mjs index 1dfe1e3..f0a6f67 100644 --- a/src/inc/routes/apiv2/index.mjs +++ b/src/inc/routes/apiv2/index.mjs @@ -1082,12 +1082,10 @@ export default router => { VALUES (${itemid}, ${newRatingId}, ${req.session.id}) `; - // If switching to NSFW/NSFL, ensure blurred thumbnail exists - if (newRatingId === 2 || newRatingId === nsfl_id) { - await queue.genBlurredThumbnail(itemid).catch(err => { - console.error(`[RATING_TOGGLE] Blurred thumbnail generation failed for ${itemid}:`, err); - }); - } + // Ensure blurred thumbnail exists + await queue.genBlurredThumbnail(itemid).catch(err => { + console.error(`[RATING_TOGGLE] Blurred thumbnail generation failed for ${itemid}:`, err); + }); }); const newRating = newRatingId === 1 ? 'sfw' : (newRatingId === 2 ? 'nsfw' : 'nsfl'); diff --git a/src/inc/routes/apiv2/tags.mjs b/src/inc/routes/apiv2/tags.mjs index f95618d..0ff5bc5 100644 --- a/src/inc/routes/apiv2/tags.mjs +++ b/src/inc/routes/apiv2/tags.mjs @@ -115,14 +115,12 @@ export default router => { await db`INSERT INTO tags_assign ${db({ tag_id: nextTagId, item_id: postid, user_id: +req.session.id })}`; } - // Automatically generate blurred thumbnail if cycling TO NSFW or NSFL - if (nextTagId === 2 || nextTagId === nsflId) { - const blurPath = path.join(cfg.paths.t, `${postid}_blur.webp`); - try { - await fs.promises.access(blurPath); - } catch { - await queue.genBlurredThumbnail(postid, false); - } + // Automatically generate/verify blurred thumbnail on cycle + const blurPath = path.join(cfg.paths.t, `${postid}_blur.webp`); + try { + await fs.promises.access(blurPath); + } catch { + await queue.genBlurredThumbnail(postid, false); } const labels = { 1: { label: 'SFW', cls: 'sfw' }, 2: { label: 'NSFW', cls: 'nsfw' }, [nsflId]: { label: 'NSFL', cls: 'nsfl' } }; @@ -180,16 +178,13 @@ export default router => { await audit.log(req.session.id, 'toggle_tag', 'item', postid, auditDetails); - // Generate blurred thumbnail if toggling TO NSFW - if (hasSFW && !hasNSFW) { - // Was SFW, now NSFW - check if blur exists and generate if not - const blurPath = path.join(cfg.paths.t, `${postid}_blur.webp`); - try { - await fs.promises.access(blurPath); - } catch { - // Doesn't exist - generate it - await queue.genBlurredThumbnail(postid, false); - } + // Ensure blurred thumbnail exists on toggle + const blurPath = path.join(cfg.paths.t, `${postid}_blur.webp`); + try { + await fs.promises.access(blurPath); + } catch { + // Doesn't exist - generate it + await queue.genBlurredThumbnail(postid, false); } const freshTags = await lib.getTags(postid); diff --git a/src/inc/routes/apiv2/upload.mjs b/src/inc/routes/apiv2/upload.mjs index b8467b8..1f518ae 100644 --- a/src/inc/routes/apiv2/upload.mjs +++ b/src/inc/routes/apiv2/upload.mjs @@ -301,9 +301,7 @@ export default router => { if (effectiveRating) { const ratingTagId = effectiveRating === 'sfw' ? 1 : (effectiveRating === 'nsfw' ? 2 : (cfg.nsfl_tag_id || 3)); await db`insert into tags_assign ${db({ item_id: itemid, tag_id: ratingTagId, user_id: req.session.id })} on conflict do nothing`; - if (effectiveRating === 'nsfw' || effectiveRating === 'nsfl') { - await queue.genBlurredThumbnail(itemid, isApprovalRequired).catch(() => {}); - } + await queue.genBlurredThumbnail(itemid, isApprovalRequired).catch(() => {}); } // Assign user tags + auto-tags @@ -575,7 +573,7 @@ export default router => { try { await queue.genThumbnail(filename, mime, itemid, url, isApprovalRequired); - if (effectiveRating === 'nsfw' || effectiveRating === 'nsfl') await queue.genBlurredThumbnail(itemid, isApprovalRequired); + await queue.genBlurredThumbnail(itemid, isApprovalRequired); } catch (err) { const tDir = isApprovalRequired ? path.join(cfg.paths.pending, 't') : cfg.paths.t; await queue.spawn('magick', ['-size', '128x128', 'xc:#1a1a1a', path.join(tDir, `${itemid}.webp`)]).catch(() => {}); diff --git a/src/inc/routes/external.mjs b/src/inc/routes/external.mjs index 63e4391..53e4f3c 100644 --- a/src/inc/routes/external.mjs +++ b/src/inc/routes/external.mjs @@ -449,7 +449,7 @@ export default (router) => { // Process thumbnail try { await queue.genThumbnail(filename, mime, itemid, url, isApprovalRequired); - if (rating === 'nsfw' || rating === 'nsfl') await queue.genBlurredThumbnail(itemid, isApprovalRequired); + await queue.genBlurredThumbnail(itemid, isApprovalRequired); } catch (err) { console.error('[REHOST] Thumbnail error:', err); } diff --git a/src/inc/trigger/parser.mjs b/src/inc/trigger/parser.mjs index 4c8be85..f703792 100644 --- a/src/inc/trigger/parser.mjs +++ b/src/inc/trigger/parser.mjs @@ -705,7 +705,7 @@ export default async bot => { // Generate Thumbnail try { await queue.genThumbnail(filename, mime, itemid, link, manualApproval); - if (isNSFW) await queue.genBlurredThumbnail(itemid, manualApproval); + await queue.genBlurredThumbnail(itemid, manualApproval); } catch (err) { const tDir = manualApproval ? path.join(cfg.paths.pending, 't') : cfg.paths.t; await queue.spawn('magick', ['./mugge.png', path.join(tDir, `${itemid}.webp`)]); @@ -815,7 +815,7 @@ export default async bot => { // Generate Thumbnail try { await queue.genThumbnail(filename, mime, itemid, link, manualApproval); - if (isNSFW) await queue.genBlurredThumbnail(itemid, manualApproval); + await queue.genBlurredThumbnail(itemid, manualApproval); } catch (err) { const tDir = manualApproval ? path.join(cfg.paths.pending, 't') : cfg.paths.t; await queue.spawn('magick', ['./mugge.png', path.join(tDir, `${itemid}.webp`)]); diff --git a/src/rethumb_handler.mjs b/src/rethumb_handler.mjs index 1f72970..7b12b7a 100644 --- a/src/rethumb_handler.mjs +++ b/src/rethumb_handler.mjs @@ -130,16 +130,8 @@ export const handleRethumbUpload = async (req, res, itemId) => { try { await execFile('magick', [tmpPath, '-coalesce', '-resize', `${thumbSpec}^`, '-gravity', 'center', '-crop', `${thumbSpec}+0+0`, '+repage', finalPath]); - // Check if item contains NSFW or NSFL tag - const tags = await db` - select tag_id from tags_assign - where item_id = ${+item.id} - and tag_id in (2, ${cfg.nsfl_tag_id || 3}) - `; - if (tags.length > 0) { - // Generate blurred thumbnail - await queue.genBlurredThumbnail(item.id, !item.active); - } + // Generate blurred thumbnail + await queue.genBlurredThumbnail(item.id, !item.active); } catch (err) { console.error('[RETHUMB HANDLER] Magick error:', err); diff --git a/src/upload_handler.mjs b/src/upload_handler.mjs index cd2f41d..0b3fc0f 100644 --- a/src/upload_handler.mjs +++ b/src/upload_handler.mjs @@ -406,10 +406,8 @@ export const handleUpload = async (req, res, self) => { } } - // Generate blurred thumbnail for NSFW/NSFL - if (effectiveRating === 'nsfw' || effectiveRating === 'nsfl') { - await queue.genBlurredThumbnail(itemid, isPending); - } + // Generate blurred thumbnail for all posts (SFW, NSFW, NSFL, Untagged) + await queue.genBlurredThumbnail(itemid, isPending); // Insert optional first comment if (comment && comment.length > 0) { @@ -553,14 +551,12 @@ export const handleUpload = async (req, res, self) => { console.error(`[BACKGROUND ERROR] genThumbnail failed for item ${itemid}:`, err); } - // Ensure blurred thumbnail exists if needed - if (effectiveRating === 'nsfw' || effectiveRating === 'nsfl') { - const tDir = isPending ? path.join(cfg.paths.pending, 't') : cfg.paths.t; - const blurPath = path.join(tDir, `${itemid}_blur.webp`); - const blurExists = await fs.access(blurPath).then(() => true).catch(() => false); - if (!blurExists) { - await queue.genBlurredThumbnail(itemid, isPending).catch(err => console.error(`[BACKGROUND ERROR] genBlurredThumbnail failed:`, err)); - } + // Ensure blurred thumbnail exists + const tDir = isPending ? path.join(cfg.paths.pending, 't') : cfg.paths.t; + const blurPath = path.join(tDir, `${itemid}_blur.webp`); + const blurExists = await fs.access(blurPath).then(() => true).catch(() => false); + if (!blurExists) { + await queue.genBlurredThumbnail(itemid, isPending).catch(err => console.error(`[BACKGROUND ERROR] genBlurredThumbnail failed:`, err)); } // Note: video title metadata is surfaced to the user as a suggestion in the upload form.