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