gsdf
This commit is contained in:
@@ -2517,6 +2517,178 @@ const f0cklib = {
|
||||
return combined.slice(0, maxLimit);
|
||||
},
|
||||
|
||||
getTagFeedItems: async ({
|
||||
tag,
|
||||
order = 'desc', // 'desc' = newest first (default), 'asc' = oldest first (chronological)
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
focus_id = null,
|
||||
mode,
|
||||
ratings,
|
||||
session,
|
||||
exclude,
|
||||
user_id,
|
||||
is_admin,
|
||||
mime
|
||||
} = {}) => {
|
||||
if (!tag || !tag.trim()) return { items: [], total: 0 };
|
||||
|
||||
const ratingsArr = (Array.isArray(ratings) && ratings.length > 0) ? ratings : null;
|
||||
const modequery = computeBaseMode(mode, ratingsArr, session);
|
||||
const globalfilter = !session ? getGlobalfilter() : null;
|
||||
const excludedTags = session && exclude ? (exclude || []) : [];
|
||||
const maxLimit = Math.min(Math.max(1, Number(limit) || 20), 50);
|
||||
const numOffset = Math.max(0, Number(offset) || 0);
|
||||
const isOwnerOrAdmin = session && is_admin;
|
||||
|
||||
const visibilityFilter = isOwnerOrAdmin
|
||||
? db``
|
||||
: (session && user_id
|
||||
? db`AND (COALESCE(items.visibility, 0) = 0 OR items.username = (SELECT "user" FROM "user" WHERE id = ${user_id}))`
|
||||
: db`AND COALESCE(items.visibility, 0) = 0`);
|
||||
|
||||
const mimeParts = (mime || "").split(',').filter(m => ['video', 'audio', 'image', 'flash', 'pdf'].includes(m));
|
||||
const mimeSQL = mimeParts.length > 0
|
||||
? db`and (${mimeParts.map(m => m === 'flash'
|
||||
? (flashMimes.length > 0
|
||||
? flashMimes.map(fm => db`items.mime = ${fm}`).reduce((a, b) => db`${a} or ${b}`)
|
||||
: db`false`)
|
||||
: (m === 'pdf' ? db`items.mime = 'application/pdf'` : db`items.mime ilike ${m + '/%'}`)).reduce((a, b) => db`${a} or ${b}`)})`
|
||||
: db``;
|
||||
|
||||
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
||||
const tagConditions = terms.map(term => {
|
||||
return db`and items.id in (
|
||||
select ta.item_id
|
||||
from tags_assign ta
|
||||
join tags t on t.id = ta.tag_id
|
||||
where t.normalized like '%' || slugify(${term}) || '%'
|
||||
)`;
|
||||
});
|
||||
|
||||
const isDesc = String(order).toLowerCase() === 'desc';
|
||||
const sortOrderItems = isDesc ? db`items.id desc` : db`items.id asc`;
|
||||
const sortOrderMi = isDesc ? db`mi.id desc` : db`mi.id asc`;
|
||||
|
||||
let computedOffset = numOffset;
|
||||
let computedLimit = maxLimit;
|
||||
|
||||
if (focus_id) {
|
||||
const numFocusId = await resolveNumericItemId(focus_id);
|
||||
if (numFocusId) {
|
||||
try {
|
||||
const rankRes = await db`
|
||||
WITH ordered AS (
|
||||
SELECT items.id, ROW_NUMBER() OVER (ORDER BY ${sortOrderItems}) - 1 as row_num
|
||||
FROM items
|
||||
WHERE items.active = true
|
||||
AND (items.is_deleted IS NOT TRUE)
|
||||
${visibilityFilter}
|
||||
${mimeSQL}
|
||||
AND ${db.unsafe(modequery)}
|
||||
${globalfilter ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND (${db.unsafe(globalfilter)}))` : db``}
|
||||
${excludedTags.length > 0 ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND tag_id = ANY(${excludedTags}::int[]))` : db``}
|
||||
${tagConditions}
|
||||
)
|
||||
SELECT row_num FROM ordered WHERE id = ${numFocusId} LIMIT 1
|
||||
`;
|
||||
if (rankRes.length > 0) {
|
||||
const rowNum = parseInt(rankRes[0].row_num, 10);
|
||||
if (rowNum < 30) {
|
||||
computedOffset = 0;
|
||||
computedLimit = Math.min(50, Math.max(20, rowNum + 10));
|
||||
} else {
|
||||
computedOffset = Math.max(0, rowNum - 6);
|
||||
computedLimit = 25;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[TAG-FEED] Error computing focus_id offset:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const countRes = await db`
|
||||
SELECT count(distinct items.id) as total
|
||||
FROM items
|
||||
WHERE items.active = true
|
||||
AND (items.is_deleted IS NOT TRUE)
|
||||
${visibilityFilter}
|
||||
${mimeSQL}
|
||||
AND ${db.unsafe(modequery)}
|
||||
${globalfilter ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND (${db.unsafe(globalfilter)}))` : db``}
|
||||
${excludedTags.length > 0 ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND tag_id = ANY(${excludedTags}::int[]))` : db``}
|
||||
${tagConditions}
|
||||
`;
|
||||
const total = parseInt(countRes[0]?.total || 0, 10);
|
||||
|
||||
const rows = await db`
|
||||
WITH matched_items AS (
|
||||
SELECT items.id, items.title, items.slug, items.mime, items.dest, items.username, items.stamp, items.xd_score, items.width, items.height, items.has_coverart
|
||||
FROM items
|
||||
WHERE items.active = true
|
||||
AND (items.is_deleted IS NOT TRUE)
|
||||
${visibilityFilter}
|
||||
${mimeSQL}
|
||||
AND ${db.unsafe(modequery)}
|
||||
${globalfilter ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND (${db.unsafe(globalfilter)}))` : db``}
|
||||
${excludedTags.length > 0 ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND tag_id = ANY(${excludedTags}::int[]))` : db``}
|
||||
${tagConditions}
|
||||
ORDER BY ${sortOrderItems}
|
||||
OFFSET ${computedOffset}
|
||||
LIMIT ${computedLimit}
|
||||
)
|
||||
SELECT
|
||||
mi.*,
|
||||
uo.display_name,
|
||||
uo.username_color,
|
||||
uo.avatar,
|
||||
uo.avatar_file,
|
||||
(SELECT ta.tag_id FROM tags_assign ta WHERE ta.item_id = mi.id AND ta.tag_id = ANY(${[1, 2, cfg.nsfl_tag_id || 3]}::int[]) LIMIT 1) as rating_tag_id,
|
||||
ARRAY(
|
||||
SELECT t.tag
|
||||
FROM tags_assign ta
|
||||
JOIN tags t ON t.id = ta.tag_id
|
||||
WHERE ta.item_id = mi.id AND ta.tag_id NOT IN (1, 2, 3)
|
||||
LIMIT 3
|
||||
) as tags
|
||||
FROM matched_items mi
|
||||
LEFT JOIN "user" u ON LOWER(u."user") = LOWER(mi.username)
|
||||
LEFT JOIN user_options uo ON uo.user_id = u.id
|
||||
ORDER BY ${sortOrderMi}
|
||||
`;
|
||||
|
||||
const items = rows.map(r => {
|
||||
const meta = xdScoreMeta(r.xd_score);
|
||||
const tagId = r.rating_tag_id;
|
||||
const ratingClass = tagId === 1 ? 'sfw' : (tagId === 2 ? 'nsfw' : (tagId === 3 ? 'nsfl' : 'untagged'));
|
||||
return {
|
||||
id: r.id,
|
||||
title: r.title || null,
|
||||
slug: r.slug || null,
|
||||
mime: r.mime,
|
||||
dest: r.dest,
|
||||
username: r.username,
|
||||
display_name: r.display_name || r.username,
|
||||
username_color: r.username_color || null,
|
||||
avatar: r.avatar || null,
|
||||
avatar_file: r.avatar_file || null,
|
||||
stamp: r.stamp,
|
||||
tags: r.tags || [],
|
||||
rating_tag_id: tagId,
|
||||
rating_class: ratingClass,
|
||||
xd_score: r.xd_score,
|
||||
xd_tier: meta.tier,
|
||||
xd_label: meta.label,
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
has_coverart: !!r.has_coverart
|
||||
};
|
||||
});
|
||||
|
||||
return { items, total, offset: computedOffset, limit: computedLimit };
|
||||
},
|
||||
|
||||
|
||||
computeBaseMode,
|
||||
getGlobalfilter,
|
||||
|
||||
Reference in New Issue
Block a user