fixing url uploads in shitpost mode

This commit is contained in:
2026-05-13 06:58:19 +02:00
parent 1fb679ecd6
commit 0b88e5c0e4
3 changed files with 76 additions and 37 deletions

View File

@@ -419,6 +419,28 @@ window.initUploadForm = (selector) => {
updateSubmitButton();
});
// In shitpost mode: auto-commit each pasted URL to the list immediately
urlInput.addEventListener('paste', (e) => {
if (!isShitpost) return;
e.preventDefault();
const pasted = (e.clipboardData || window.clipboardData).getData('text');
const lines = pasted.split(/[\n\r]+/).map(u => u.trim()).filter(u => /^https?:\/\//i.test(u));
if (!lines.length) {
// Not a URL — let the browser insert it normally into the input
urlInput.value = pasted.trim();
urlInput.dispatchEvent(new Event('input'));
return;
}
lines.forEach(url => {
if (!selectedFiles.some(item => item.type === 'url' && item.url === url)) {
selectedFiles.push({ type: 'url', url, rating: '', tags: [], comment: '', is_oc: false });
}
});
urlInput.value = '';
if (urlBadge) urlBadge.style.display = 'none';
handleFile();
});
if (urlBadge) {
urlBadge.addEventListener('click', () => {
const val = urlInput.value.trim();
@@ -429,30 +451,30 @@ window.initUploadForm = (selector) => {
});
}
// Add URL button (shitpost mode) — commits the typed/pasted URL to the list
const addUrlFn = () => {
const val = urlInput.value.trim();
if (!val || !/^https?:\/\//i.test(val)) return;
if (!selectedFiles.some(item => item.type === 'url' && item.url === val)) {
selectedFiles.push({ type: 'url', url: val, rating: '', tags: [], comment: '', is_oc: false });
}
urlInput.value = '';
if (urlBadge) urlBadge.style.display = 'none';
handleFile();
};
const btnAddUrls = form.querySelector('.btn-add-urls');
if (btnAddUrls) {
btnAddUrls.addEventListener('click', () => {
const val = urlInput.value.trim();
if (!val) return;
const lines = val.split('\n').map(u => u.trim()).filter(u => u.length > 0);
lines.forEach(url => {
if (/^https?:\/\//i.test(url)) {
if (!selectedFiles.some(item => item.type === 'url' && item.url === url)) {
selectedFiles.push({
type: 'url',
url: url,
rating: '',
tags: [],
comment: '',
is_oc: false
});
}
}
});
urlInput.value = '';
handleFile();
btnAddUrls.addEventListener('click', addUrlFn);
}
// Also commit on Enter key inside the URL input in shitpost mode
if (isShitpost) {
urlInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') { e.preventDefault(); addUrlFn(); }
});
}
}
const formatSize = (bytes) => {
@@ -1653,7 +1675,8 @@ window.initUploadForm = (selector) => {
rating: fileRating,
tags: fileTags.join(','),
is_oc: (isShitpost ? item.is_oc : isOc),
comment: fileComment
comment: fileComment,
is_shitpost: isShitpost ? true : undefined
}));
} else {
xhr.send(formData);
@@ -1663,6 +1686,12 @@ window.initUploadForm = (selector) => {
if (res.success) {
successCount++;
lastData = res;
if (res.pending) {
// Background URL download — show toast using server's message
if (typeof window.flashMessage === 'function') {
window.flashMessage(res.msg, 4000, 'info');
}
}
if (res.itemid) {
try {
const ts = Date.now();

View File

@@ -202,20 +202,25 @@ export default router => {
return res.json({ success: false, msg: 'URL uploads are disabled' }, 403);
}
const { url: inputUrl, rating, tags: tagsRaw, comment, is_oc } = req.post || {};
const { url: inputUrl, rating, tags: tagsRaw, comment, is_oc, is_shitpost } = req.post || {};
if (!inputUrl || !inputUrl.trim()) {
return res.json({ success: false, msg: 'URL is required' }, 400);
}
if (!rating || !['sfw', 'nsfw', 'nsfl'].includes(rating)) {
// In shitpost mode rating is optional; null = no rating tag assigned
const effectiveRating = (rating && ['sfw', 'nsfw', 'nsfl'].includes(rating)) ? rating : (is_shitpost ? null : null);
if (!is_shitpost && !effectiveRating) {
return res.json({ success: false, msg: 'Rating (sfw/nsfw/nsfl) is required' }, 400);
}
if (rating === 'nsfl' && !cfg.enable_nsfl) {
if (effectiveRating === 'nsfl' && !cfg.enable_nsfl) {
return res.json({ success: false, msg: 'NSFL mode is currently disabled' }, 400);
}
const tags = tagsRaw ? tagsRaw.split(',').map(t => t.trim()).filter(t => t.length > 0 && !['sfw', 'nsfw', 'nsfl'].includes(t.toLowerCase())) : [];
const minTags = getMinTags();
if (tags.length < minTags) {
// In shitpost mode tags are optional
if (!is_shitpost && tags.length < minTags) {
return res.json({ success: false, msg: `At least ${minTags} tag${minTags !== 1 ? 's' : ''} required` }, 400);
}
@@ -287,11 +292,13 @@ export default router => {
await queue.spawn('magick', ['-size', '128x128', 'xc:#1a1a1a', '-gravity', 'center', '-fill', '#666', '-pointsize', '20', '-annotate', '0', 'YouTube', path.join(tDir, `${itemid}.webp`)]).catch(() => {});
}
// Assign rating tag
const ratingTagId = rating === 'sfw' ? 1 : (rating === '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 (rating === 'nsfw' || rating === 'nsfl') {
await queue.genBlurredThumbnail(itemid, isApprovalRequired).catch(() => {});
// Assign rating tag (only if a rating was selected)
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(() => {});
}
}
// Assign user tags + auto-tags
@@ -563,14 +570,17 @@ export default router => {
try {
await queue.genThumbnail(filename, mime, itemid, url, isApprovalRequired);
if (rating === 'nsfw' || rating === 'nsfl') await queue.genBlurredThumbnail(itemid, isApprovalRequired);
if (effectiveRating === 'nsfw' || effectiveRating === 'nsfl') 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(() => {});
}
const ratingTagId = rating === 'sfw' ? 1 : (rating === 'nsfw' ? 2 : (cfg.nsfl_tag_id || 3));
await db`insert into tags_assign ${db({ item_id: itemid, tag_id: ratingTagId, user_id: session.id })}`;
// Assign rating tag (only if a rating was selected)
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: session.id })}`;
}
const autoTags = autoTagsFromUrl(url);
const allTags = [...new Set([...tags, ...autoTags])];
for (const tagName of allTags) {
@@ -594,7 +604,7 @@ export default router => {
mime: mime,
username: session.user,
display_name: session.display_name || null,
tag_id: rating === 'sfw' ? 1 : (rating === 'nsfw' ? 2 : (cfg.nsfl_tag_id || 3)),
tag_id: effectiveRating ? (effectiveRating === 'sfw' ? 1 : (effectiveRating === 'nsfw' ? 2 : (cfg.nsfl_tag_id || 3))) : 0,
is_oc: !!is_oc
})})`;
} catch (err) {

View File

@@ -40,10 +40,10 @@
<div class="upload-mode-content" id="mode-url" style="display: none;">
<div class="url-input-container">
@if(shitpost_mode)
<div class="url-shitpost-container">
<textarea id="url-upload-input" name="url" placeholder="{{ t('upload.url_placeholder_shitpost') || 'Paste multiple URLs here (one per line)...' }}" autocomplete="off" style="width: 100%; min-height: 100px; background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); color: #fff; padding: 10px; border-radius: 4px; resize: vertical;"></textarea>
<div class="url-shitpost-container" style="display: flex; flex-direction: column; gap: 6px;">
<input type="url" id="url-upload-input" name="url" placeholder="Paste or type a URL..." autocomplete="off" style="width: 100%;">
<button type="button" class="btn-add-urls">
<i class="fa-solid fa-plus-circle"></i> {{ t('upload.btn_add_urls') }}
<i class="fa-solid fa-plus-circle"></i> Add
</button>
</div>
@else