diff --git a/public/s/js/scroller.js b/public/s/js/scroller.js index 3e62e74..c6e7e65 100644 --- a/public/s/js/scroller.js +++ b/public/s/js/scroller.js @@ -1456,7 +1456,7 @@ ? `/${item.external_board}/thread/${item.external_tid}` : (item.local_id ? `#${item.local_id}` - : `#${item.id}`)} + : `#${item.id}`)} `; slide.appendChild(meta); @@ -1636,8 +1636,8 @@ const localId = slide?.dataset.localId; const id = localId || shareBtn.dataset.id; const abyssUrl = localId - ? `${location.origin}/abyss#${localId}` - : `${location.origin}/abyss#${id}`; + ? `${location.origin}/abyss/${localId}` + : (id ? `${location.origin}/abyss/${id}` : `${location.origin}/abyss`); openSharePanel(abyssUrl); }); const rehostBtn = actions.querySelector('.rehost-btn'); @@ -3139,7 +3139,7 @@ }); } - // ── Auto-load 4chan thread from hash (e.g. #wsg/6132740) ──────────────── + // ── Auto-load 4chan thread from path or hash (e.g. /abyss/wsg/6211653 or #wsg/6211653) ──────────────── let chanHashPending = null; // async promise if we need server lookup { const hid = hashId(); @@ -3153,21 +3153,29 @@ const postMap = JSON.parse(localStorage.getItem('4chan_post_threads') || '{}'); tid = postMap[hid]; // hid is "board/postno" } catch(_) {} - if (tid) { - applied.externalUrl = `https://boards.4chan.org/${board}/thread/${tid}`; - applied.order = 'oldest'; - unlock4chan(); - } else { - // No local mapping — ask the server to find the thread + + const effectiveTid = tid || postno; + applied.externalUrl = `https://boards.4chan.org/${board}/thread/${effectiveTid}`; + applied.order = 'oldest'; + unlock4chan(); + + if (!tid) { + // In case postno was a reply post rather than thread OP, query find in background chanHashPending = fetch(`/api/v2/scroller/external/4chan/${board}/find/${postno}`) .then(r => r.json()) .then(data => { - window.f0ckDebug('[CHAN] Find result:', data); + window.f0ckDebug && window.f0ckDebug('[CHAN] Find result:', data); if (data.success && data.tid) { - applied.externalUrl = `https://boards.4chan.org/${board}/thread/${data.tid}`; - applied.order = 'oldest'; - unlock4chan(); - window.f0ckDebug('[CHAN] Set externalUrl:', applied.externalUrl); + try { + const postMap = JSON.parse(localStorage.getItem('4chan_post_threads') || '{}'); + postMap[hid] = data.tid; + localStorage.setItem('4chan_post_threads', JSON.stringify(postMap)); + } catch(_) {} + if (String(data.tid) !== String(effectiveTid)) { + applied.externalUrl = `https://boards.4chan.org/${board}/thread/${data.tid}`; + applied.order = 'oldest'; + reloadFeed(); + } } }) .catch(err => { console.error('[CHAN] Find error:', err); }); @@ -3923,7 +3931,7 @@ const restored = tryRestoreFromCache(); if (!restored) { - if (chanHashPending) { + if (chanHashPending && !applied.externalUrl) { chanHashPending.then(() => fetchItems()); } else { fetchItems(); diff --git a/src/inc/lib.mjs b/src/inc/lib.mjs index b9480f4..71a16e9 100644 --- a/src/inc/lib.mjs +++ b/src/inc/lib.mjs @@ -545,15 +545,16 @@ export default new class { }; async chanAuth(req, res, next) { + const isApi = (req.url?.pathname && req.url.pathname.startsWith('/api/')) || req.headers['x-requested-with'] === 'XMLHttpRequest' || req.headers['accept']?.includes('application/json'); if (!req.session || !req.session.user) { - if (req.headers['x-requested-with'] === 'XMLHttpRequest') { + if (isApi) { return res.reply({ code: 401, body: JSON.stringify({ success: false, msg: "Unauthorized" }), type: 'application/json' }); } return res.redirect('/login'); } const hasGroup = req.session.admin || (Array.isArray(req.session.groups) && req.session.groups.includes('4chan')); if (!hasGroup) { - if (req.headers['x-requested-with'] === 'XMLHttpRequest') { + if (isApi) { return res.reply({ code: 403, body: JSON.stringify({ success: false, msg: "4chan group required" }), type: 'application/json' }); } return res.reply({ diff --git a/src/inc/routes/scroller.mjs b/src/inc/routes/scroller.mjs index 8721830..b6531af 100644 --- a/src/inc/routes/scroller.mjs +++ b/src/inc/routes/scroller.mjs @@ -5,6 +5,12 @@ import f0cklib from "../routeinc/f0cklib.mjs"; import { isAnonymizeSession, isAnonSession } from "../settings.mjs"; export default (router, tpl) => { + // Redirect /scroller to /abyss (preserving ID or fragment) + router.get(/^\/scroller(?:\/(?[a-zA-Z0-9_\/-]+))?\/?$/, (req, res) => { + const id = req.params?.id || req.params?.[0]; + return res.redirect(id ? `/abyss/${id}` : '/abyss'); + }); + // Serve the scroller page router.get(/^\/abyss(?:\/(?[a-zA-Z0-9_\/-]+))?\/?$/, async (req, res) => { if (cfg.websrv.abyss_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) }); @@ -48,6 +54,12 @@ export default (router, tpl) => { } catch (e) { console.error('[SCROLLER] Failed to fetch meta for ID:', id, e); } + } else if (id && /^([a-z0-9]+)\/(\d+)$/i.test(id.trim())) { + const chanMatch = id.trim().match(/^([a-z0-9]+)\/(\d+)$/i); + const [, board, tid] = chanMatch; + page_meta.title = `/${board}/${tid} — Abyss`; + page_meta.description = `Watch /${board}/ thread ${tid} in Abyss`; + page_meta.url = `https://${cfg.main.url.domain}/abyss/${board}/${tid}`; } return res.reply({ diff --git a/views/chan/thread.html b/views/chan/thread.html index 296fd54..0f6d8cc 100644 --- a/views/chan/thread.html +++ b/views/chan/thread.html @@ -14,8 +14,8 @@ Player View @endif - - Scroller + + Abyss 4chan.org