diff --git a/config_example.json b/config_example.json index 445093f..3fc91b6 100644 --- a/config_example.json +++ b/config_example.json @@ -86,6 +86,8 @@ "web_meta_extraction": true, "bypass_duplicate_check": true, "shitpost_mode": false, + "shitpost_require_rating": false, + "shitpost_min_tags": 0, "protect_files": false, "enable_dynamic_thumbs": false, "allowed_comment_images": [ diff --git a/public/s/js/upload.js b/public/s/js/upload.js index de8d5bf..406f2f7 100644 --- a/public/s/js/upload.js +++ b/public/s/js/upload.js @@ -74,6 +74,9 @@ window.initUploadForm = (selector) => { const commentMaxLen = (commentMaxLenAttr && commentMaxLenAttr !== 'null') ? parseInt(commentMaxLenAttr) : null; const isShitpost = form.classList.contains('shitpost-mode-active') || !!window.f0ckShitpostMode; + // Config-driven shitpost overrides + const shitpostRequireRating = isShitpost && !!window.f0ckShitpostRequireRating; + const shitpostMinTags = isShitpost ? (parseInt(window.f0ckShitpostMinTags) || 0) : 0; let tags = []; let autoTags = []; // Track tags suggested from metadata let selectedFiles = []; // Array of files for shitpost_mode @@ -548,14 +551,23 @@ window.initUploadForm = (selector) => { const isShitpost = !!window.f0ckShitpostMode; const rating = form.querySelector('input[name="rating"]:checked'); - // In Shitpost Mode, ratings are per-item (optional) and tags are optional — just need files - const hasRating = (isShitpost && activeMode === 'file') ? true : (rating !== null); + // In Shitpost Mode, ratings are per-item. If require rating is true, every item must be rated. + let hasRating = true; + if (isShitpost && activeMode === 'file') { + if (shitpostRequireRating) { + hasRating = selectedFiles.length > 0 && selectedFiles.every(item => ['sfw', 'nsfw', 'nsfl'].includes(item.rating)); + } + } else { + hasRating = (rating !== null); + } let hasTags = true; if (!isShitpost) { hasTags = tags.length >= minTags; + } else if (shitpostMinTags > 0 && activeMode === 'file') { + // In shitpost file mode with min-tags enforced: every queued item must meet the threshold. + hasTags = selectedFiles.length === 0 || selectedFiles.every(item => (item.tags || []).length >= shitpostMinTags); } - // In shitpost file mode: hasTags is always true (untagged is allowed) // Toggle visibility of global rating/comment/tag sections const ratingSec = form.querySelector('.global-rating-section'); @@ -606,18 +618,27 @@ window.initUploadForm = (selector) => { ? (ssrSelectFileText || i18n.select_file || 'Select a file') : (i18n.enter_url || 'Enter a URL'); } else if (!hasTags) { - // non-shitpost only - const remaining = minTags - tags.length; - const tpl = i18n.tags_required || '{n} more tag{s} required'; - btnText.textContent = tpl - .replace('{n}', remaining) - .replace('{s}', remaining !== 1 ? 's' : ''); + // non-shitpost or shitpost with min-tags + if (isShitpost && shitpostMinTags > 0) { + const remaining = shitpostMinTags - Math.min(...selectedFiles.map(item => (item.tags || []).length)); + btnText.textContent = `${remaining} more tag${remaining !== 1 ? 's' : ''} required per item`; + } else { + const remaining = minTags - tags.length; + const tpl = i18n.tags_required || '{n} more tag{s} required'; + btnText.textContent = tpl + .replace('{n}', remaining) + .replace('{s}', remaining !== 1 ? 's' : ''); + } } else if (!hasRating) { const nsflEnabled = !!form.querySelector('input[name="rating"][value="nsfl"]'); - if (nsflEnabled) { - btnText.textContent = i18n.select_rating_nsfl || 'Select SFW, NSFW or NSFL'; + if (isShitpost && shitpostRequireRating) { + btnText.textContent = 'Select a rating for each item'; } else { - btnText.textContent = i18n.select_rating || 'Select SFW or NSFW'; + if (nsflEnabled) { + btnText.textContent = i18n.select_rating_nsfl || 'Select SFW, NSFW or NSFL'; + } else { + btnText.textContent = i18n.select_rating || 'Select SFW or NSFW'; + } } } else { if (activeMode === 'url' && urlInput && ytRegex.test(urlInput.value.trim()) && window.f0ckEnableYoutubeUpload !== false) { @@ -841,19 +862,21 @@ window.initUploadForm = (selector) => { let commentUI = ''; if (isShitpost) { const nsflEnabled = !!form.querySelector('input[name="rating"][value="nsfl"]'); + // Build per-item rating HTML + const ratingValue = item.rating; ratingSwitch = `