This commit is contained in:
2026-09-13 20:15:48 +02:00
parent ffd32a6b63
commit 399f2be456
24 changed files with 2159 additions and 90 deletions
+138 -6
View File
@@ -119,7 +119,9 @@ const resolveNumericItemId = async (itemIdOrSlug) => {
if (/^\d+$/.test(String(itemIdOrSlug))) return parseInt(itemIdOrSlug, 10);
try {
const rows = await db`SELECT id FROM items WHERE slug = ${String(itemIdOrSlug)} LIMIT 1`;
return rows[0]?.id || null;
if (rows[0]?.id) return rows[0].id;
const subRows = await db`SELECT item_id FROM album_items WHERE slug = ${String(itemIdOrSlug)} LIMIT 1`;
return subRows[0]?.item_id || null;
} catch (e) {
return null;
}
@@ -770,6 +772,8 @@ const f0cklib = {
items.is_oc,
items.xd_score,
items.has_coverart,
items.is_album,
items.album_count,
${user_id ? db`max(coalesce(uvv.view_count, 0)) as my_views,` : db``}
${user_id ? db`EXISTS (SELECT 1 FROM notifications WHERE user_id = ${user_id} AND item_id = items.id AND is_read = false) as has_notification,` : db`false as has_notification,`}
(case when min(ta.tag_id) = 1 then 'SFW' when min(ta.tag_id) = 2 then 'NSFW' else 'NSFL' end) as tag,
@@ -915,7 +919,19 @@ const f0cklib = {
}
const isNumeric = /^\d+$/.test(String(rawIdOrSlug));
const itemLookup = isNumeric ? db`items.id = ${+rawIdOrSlug}` : db`items.slug = ${String(rawIdOrSlug)}`;
let itemLookup = isNumeric ? db`items.id = ${+rawIdOrSlug}` : db`items.slug = ${String(rawIdOrSlug)}`;
let requestedSubf0ckSlug = null;
if (!isNumeric) {
const itemRow = await db`SELECT id FROM items WHERE slug = ${String(rawIdOrSlug)} LIMIT 1`;
if (!itemRow.length) {
const subRow = await db`SELECT item_id, slug FROM album_items WHERE slug = ${String(rawIdOrSlug)} LIMIT 1`;
if (subRow.length) {
requestedSubf0ckSlug = subRow[0].slug;
itemLookup = db`items.id = ${subRow[0].item_id}`;
}
}
}
const { mimeParts, mimeSQL } = resolveMimeSQL(mime, session);
const excludedTags = exclude || [];
@@ -1254,14 +1270,30 @@ const f0cklib = {
}
// Efficient coverart fallback
// Efficient coverart fallback with on-demand extraction
let hasCoverart = actitem.has_coverart;
if (!hasCoverart && actitem.mime?.startsWith('audio/')) {
const caPath = path.join(cfg.paths.ca, `${actitem.id}.webp`);
try {
if (fs.existsSync(caPath)) {
if (fs.existsSync(caPath) && fs.statSync(caPath).size > 0) {
hasCoverart = true;
db`UPDATE items SET has_coverart = TRUE WHERE id = ${actitem.id}`.catch(() => {});
} else {
// Attempt extraction directly from audio file if embedded
const sourcePath = path.join(cfg.paths.b, actitem.dest);
if (fs.existsSync(sourcePath)) {
await queue.spawn('ffmpeg', ['-y', '-i', sourcePath, '-an', '-vcodec', 'webp', '-frames:v', '1', caPath], { quiet: true }).catch(() => {});
if (fs.existsSync(caPath) && fs.statSync(caPath).size > 0) {
hasCoverart = true;
db`UPDATE items SET has_coverart = TRUE WHERE id = ${actitem.id}`.catch(() => {});
const tPath = path.join(cfg.paths.t, `${actitem.id}.webp`);
if (!fs.existsSync(tPath) || fs.statSync(tPath).size === 0) {
await queue.spawn('magick', [caPath + '[0]', '-resize', '256x256^', '-gravity', 'center', '-crop', '256x256+0+0', '+repage', tPath], { quiet: true }).catch(() => {});
}
} else {
try { fs.unlinkSync(caPath); } catch (_) {}
}
}
}
} catch (_) {}
}
@@ -1269,6 +1301,102 @@ const f0cklib = {
? `${cfg.websrv.paths.coverarts}/${actitem.id}.webp`
: `/s/img/music.webp`;
let album = [];
if (actitem.is_album) {
try {
const albumRows = await db`
SELECT id, dest, mime, size, width, height, order_index, slug, checksum
FROM album_items
WHERE item_id = ${itemid}
ORDER BY order_index ASC
`;
if (albumRows.length > 0) {
album = await Promise.all(albumRows.map(async (r, idx) => {
const order = r.order_index !== undefined && r.order_index !== null ? r.order_index : idx;
const subSlug = r.slug || r.id;
const subBase = r.dest.replace(/\.[^.]+$/, '');
const isAudio = (r.mime || '').startsWith('audio/');
let subCover = null;
let subThumb = `${cfg.websrv.paths.thumbnails}/${subBase}.webp`;
if (isAudio) {
const caFile = path.join(cfg.paths.ca, `${subBase}.webp`);
const tFile = path.join(cfg.paths.t, `${subBase}.webp`);
let caExists = false;
try {
caExists = fs.existsSync(caFile) && fs.statSync(caFile).size > 0;
} catch (_) {}
if (!caExists && order === 0 && hasCoverart) {
const parentCaFile = path.join(cfg.paths.ca, `${actitem.id}.webp`);
try {
if (fs.existsSync(parentCaFile) && fs.statSync(parentCaFile).size > 0) {
subCover = `${cfg.websrv.paths.coverarts}/${actitem.id}.webp`;
subThumb = `${cfg.websrv.paths.thumbnails}/${actitem.id}.webp`;
caExists = true;
}
} catch (_) {}
}
if (!caExists) {
const audioSource = path.join(cfg.paths.b, r.dest);
if (fs.existsSync(audioSource)) {
try {
await queue.spawn('ffmpeg', ['-y', '-i', audioSource, '-an', '-vcodec', 'webp', '-frames:v', '1', caFile], { quiet: true });
if (fs.existsSync(caFile) && fs.statSync(caFile).size > 0) {
caExists = true;
await queue.spawn('magick', [caFile + '[0]', '-resize', '256x256^', '-gravity', 'center', '-crop', '256x256+0+0', '+repage', tFile], { quiet: true });
} else {
try { fs.unlinkSync(caFile); } catch (_) {}
}
} catch (_) {}
}
}
if (caExists) {
if (!subCover) subCover = `${cfg.websrv.paths.coverarts}/${subBase}.webp`;
try {
if (!fs.existsSync(tFile) || fs.statSync(tFile).size === 0) {
subThumb = subCover;
}
} catch (_) {
subThumb = subCover;
}
} else {
subCover = '/s/img/audio.webp';
subThumb = '/s/img/audio.webp';
}
}
return {
id: r.id,
slug: r.slug,
subf0ck_id: subSlug,
dest: `${cfg.websrv.paths.images}/${r.dest}`,
src: `${cfg.websrv.paths.images}/${r.dest}`,
filename: r.dest,
mime: r.mime,
size: lib.formatSize(r.size),
width: r.width,
height: r.height,
checksum: r.checksum,
order_index: order,
display_index: order + 1,
is_first: order === 0,
is_video: (r.mime || '').startsWith('video/'),
is_audio: isAudio,
is_image: (r.mime || '').startsWith('image/'),
has_coverart: isAudio ? (subCover && subCover !== '/s/img/audio.webp') : false,
coverart: isAudio ? subCover : null,
thumb: subThumb
};
}));
}
} catch (err) {
console.error('[GETF0CK] Failed to fetch album items:', err.message);
}
}
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] [GETF0CK_OPT] Fetch complete in ${duration}ms`);
@@ -1410,8 +1538,12 @@ const f0cklib = {
height: actitem.height || null,
original_filename: actitem.original_filename || null,
expires_at: actitem.expires_at || null,
expires_in: lib.expiresIn(actitem.expires_at)
expires_in: lib.expiresIn(actitem.expires_at),
is_album: !!(actitem.is_album && album.length > 1),
album_count: album.length || actitem.album_count || 0,
album: album,
album_json: JSON.stringify(album),
requested_subf0ck_slug: requestedSubf0ckSlug || null
},
title: `${(getEnableItemSlugs() && actitem.slug) ? actitem.slug : actitem.id} - ${cfg.websrv.domain}`,
pagination: {