f0ck algos

This commit is contained in:
2026-09-12 04:25:49 +02:00
parent b55a17dc85
commit 2913b03d50
12 changed files with 1756 additions and 22 deletions
+99
View File
@@ -707,6 +707,103 @@ export default router => {
}
});
});
group.get(/\/recommendations(?:\/(?<type>videos|all))?$/, async (req, res) => {
try {
const limit = Math.min(+(req.url.qs?.limit || 20), 50);
let mode = req.mode ?? 0;
if (req.url.qs && req.url.qs.mode && ['0', '1', '2', '3'].includes(req.url.qs.mode)) {
mode = parseInt(req.url.qs.mode);
}
const ratingsRaw = req.cookies.ratings;
const ratingsArr = ratingsRaw ? decodeURIComponent(ratingsRaw).split(/[|,]/).filter(r => ['sfw','nsfw','nsfl','untagged'].includes(r)) : null;
const mime = req.params?.type === 'videos' ? 'video' : (req.url.qs?.mime || null);
let excludeIds = [];
if (req.url.qs?.exclude_ids) {
excludeIds = String(req.url.qs.exclude_ids).split(',').map(Number).filter(n => Number.isInteger(n) && n > 0).slice(0, 150);
}
const sessionTags = req.url.qs?.session_tags || '';
const sessionCreators = req.url.qs?.session_creators || '';
const items = await f0cklib.getPersonalizedRecommendations({
limit,
mode,
ratings: ratingsArr,
session: !!req.session,
exclude: req.session?.excluded_tags || [],
user_id: req.session?.id,
is_admin: req.session?.admin,
mime,
exclude_ids: excludeIds,
session_tags: sessionTags,
session_creators: sessionCreators
});
res.json({
success: true,
items,
videos: items
});
} catch (err) {
console.error("[RECOMMENDATIONS] Failed to fetch personalized recommendations:", err);
res.json({
success: false,
items: [],
videos: [],
error: "Failed to load recommendations"
});
}
});
// Track user behavioral signals (dwell time, completion, skips, suggestion clicks)
group.post(/\/track\/interaction$/, async (req, res) => {
try {
let payload = req.post || {};
if (!payload || Object.keys(payload).length === 0) {
try {
const body = await collectBody(req);
if (body && body.length > 0) payload = JSON.parse(body.toString());
} catch (_) {}
}
const itemId = parseInt(payload.item_id || payload.itemId, 10);
if (!itemId) {
return res.json({ success: false, error: "Invalid item_id" }, 400);
}
const duration = parseFloat(payload.duration) || 0;
const percent = parseFloat(payload.percent) || 0;
const type = String(payload.type || 'dwell');
// Behavioral weighting: time spent is a primary signal
let delta = 1.0;
if (type === 'skip' || (duration < 2.5 && percent < 15)) {
delta = -1.5;
} else if (type === 'click_suggestion') {
delta = 2.5;
} else if (type === 'finish' || percent >= 75 || duration >= 20) {
delta = 3.0;
} else if (duration >= 8 || percent >= 35) {
delta = 1.5;
}
if (req.session?.id) {
f0cklib.updateUserAffinity({
user_id: req.session.id,
item_id: itemId,
scoreDelta: delta
}).catch(err => console.error("[TRACK] Affinity update failed:", err));
}
return res.json({ success: true, delta });
} catch (err) {
console.error("[TRACK] Interaction error:", err);
return res.json({ success: false, error: "Tracking failed" }, 500);
}
});
group.get(/\/orakel\/user$/, async (req, res) => {
try {
@@ -1201,6 +1298,7 @@ export default router => {
where user_id = ${+req.session.id}
and item_id = ${+postid}
`;
f0cklib.updateUserAffinity({ user_id: req.session.id, item_id: +postid, scoreDelta: -5.0 }).catch(() => {});
} else {
// add fav — ON CONFLICT DO NOTHING guards against rapid double-taps
await db`
@@ -1210,6 +1308,7 @@ export default router => {
}, 'item_id', 'user_id')}
on conflict do nothing
`;
f0cklib.updateUserAffinity({ user_id: req.session.id, item_id: +postid, scoreDelta: 5.0 }).catch(() => {});
}
const favs = await db`