diff --git a/debug/thumbnailer.mjs b/debug/thumbnailer.mjs index bbce470..6339f7c 100644 --- a/debug/thumbnailer.mjs +++ b/debug/thumbnailer.mjs @@ -19,7 +19,7 @@ let items; if(_itemid > 0) items = await sql('items').where('id', _itemid); else - items = await sql('items').orderBy('id', 'asc').where('src', 'like', '%soundcloud%').where('mime', 'like', 'audio/%'); + items = await sql`select id, dest, mime, src from "items"`; let count = 1; let total = items.length; @@ -32,7 +32,7 @@ for(let item of items) { if(mime.startsWith('video/') || mime == 'image/gif') await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`); else if(mime.startsWith('image/') && mime != 'image/gif') - await exec(`convert ./public/b/${filename} ./tmp/${itemid}.png`); + await exec(`magick ./public/b/${filename} ./tmp/${itemid}.png`); else if(mime.startsWith('audio/')) { if(link.match(/soundcloud/)) { let cover = (await exec(`yt-dlp --get-thumbnail "${link}"`)).stdout.trim(); @@ -42,8 +42,8 @@ for(let item of items) { await exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`); const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size; if(size >= 0) { - await exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`); - await exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`); + await exec(`magick ./tmp/${itemid}.jpg ./tmp/${itemid}.png`); + await exec(`magick ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`); } } catch(err) { //console.log(err); @@ -51,21 +51,21 @@ for(let item of items) { } else { await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`); - await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); + await exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); } } else { await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`); - await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); + await exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); } } - await exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`); + await exec(`magick "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`); await fs.promises.unlink(`./tmp/${itemid}.png`).catch(err => {}); await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(err => {}); } catch(err) { //console.log(err); - await exec(`convert ./mugge.png ./public/t/${itemid}.webp`); + await exec(`magick ./mugge.png ./public/t/${itemid}.webp`); } console.log(`current: ${itemid} (${count} / ${total})`); count++; @@ -76,7 +76,7 @@ for(let item of items) { const itemid = item.id; const filename = item.dest; await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./debug/tmp/${itemid}`); - await exec(`convert "./debug/tmp/${itemid}" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.png`); + await exec(`magick "./debug/tmp/${itemid}" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.png`); await fs.unlink(`./debug/tmp/${itemid}`); console.log(`current: ${itemid} (${count} / ${total})`); } catch(err) {} diff --git a/f0ck.sql b/f0ck.sql index 0c0c5c1..15c2259 100644 --- a/f0ck.sql +++ b/f0ck.sql @@ -17,13 +17,13 @@ SET client_min_messages = warning; SET row_security = off; -- --- Name: public; Type: SCHEMA; Schema: -; Owner: postgres +-- Name: public; Type: SCHEMA; Schema: -; Owner: f0ck -- -- *not* creating schema, since initdb creates it -ALTER SCHEMA public OWNER TO postgres; +ALTER SCHEMA public OWNER TO f0ck; -- -- Name: unaccent; Type: EXTENSION; Schema: -; Owner: - @@ -664,7 +664,7 @@ ALTER TABLE ONLY public.user_sessions CREATE PUBLICATION alltables FOR ALL TABLES WITH (publish = 'insert, update, delete, truncate'); -ALTER PUBLICATION alltables OWNER TO postgres; +ALTER PUBLICATION alltables OWNER TO f0ck; -- -- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres diff --git a/public/s/css/f0ck.css b/public/s/css/f0ck.css index ed89d38..578f187 100644 --- a/public/s/css/f0ck.css +++ b/public/s/css/f0ck.css @@ -72,7 +72,7 @@ html[theme='p1nk'] { --black: #000; --white: #fff; --gray: #262626; - --nav-bg: #2b2b2b; + --nav-bg: #201f1f; --nav-brand-border: inset 1px #242424; --nav-brand-bg: #171717; --navigation-links-bg: #201f1f; @@ -494,7 +494,7 @@ html[theme="atmos"] { --posts-meta-bg: #000000b8; --badge-sfw: #68a728; --badge-nsfw: #a72828; - --badge-tag: #090909; + --badge-tag: #353535; --scrollbar-color: #2b2b2b; --footbar-color: #1fb2b0; --loading-indicator-color: #1fb2b0; @@ -1618,9 +1618,10 @@ span.placeholder { } @media (max-width: 1325px) { - .ranking { +/* ranking page - idea */ + /* .ranking { grid-template-columns: 1fr 1fr !important; - } + } */ .by-user, .by-stats, @@ -2401,7 +2402,7 @@ table.table tbody tr:nth-of-type(odd) { .ranking { display: grid; grid-template-columns: auto; - justify-content: center; + /* justify-content: center; */ } .ranking div { @@ -2414,15 +2415,15 @@ table.table tbody tr:nth-of-type(odd) { } .by-user { - grid-column: 1; + grid-row: 1; } .by-stats { - grid-column: 3; + grid-row: 3; } .by-hoster { - grid-column: 2; + grid-row: 2; } /* tags */ @@ -3015,4 +3016,8 @@ input#s_avatar { #s_avatar:hover { background: #ffffff0f; +} +.theforceofthree { + display: grid; + grid-template-columns: 0.4fr 1fr 0.4fr; } \ No newline at end of file diff --git a/public/s/css/w0bm.css b/public/s/css/w0bm.css new file mode 100644 index 0000000..77fa525 --- /dev/null +++ b/public/s/css/w0bm.css @@ -0,0 +1,163 @@ +.navbar-brand { + display: grid; + grid-template-columns: 1fr auto; +} + +.image-brand { + width: 5cm; +} + +.kontrollelement { + grid-column: 2; +} + +.metadata { + grid-template-columns: 0.4fr auto 0.4fr; + grid-template-rows: auto 1fr; + background-color: transparent !important; + border: none !important; +} + +html[theme="f0ck95d"] .badge-dark { + border: none; + background-image: none; + box-shadow: none; +} + +.steuerung { + font-size:x-large; + font-family: monospace; +} + +.steuerung a { + color: white; +} + +.blahlol { + grid-column: 2; +} + +span#favs { + background: transparent !important; + border: none !important; +} + +.badge-dark, #themeselector { + background-color: unset; + border: unset; +} + +._204863 { + background: none; + border: none; + background-color: none; +} + +html[theme="iced"] ._204863 { + background: none; + } + +.media-object { + border: none; +} + +.v0ck { + background: transparent !important; +} + +html[theme="f0ck95d"] .embed-responsive.embed-responsive-16by9 { + background:#0000008f !important; +} + +html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 { + background:var(--nav-bg) !important; +} + +.v0ck_overlay { + background-color: transparent; +} + +.v0ck_player_controls { + background:rgba(0, 0, 0, 0.95) !important; +} + +html[theme="term"] .image-brand { + filter: hue-rotate(-50deg); +} + +html[theme="f0ck"] .image-brand { + filter: hue-rotate(-50deg); +} + +html[theme="p1nk"] .image-brand { + filter: hue-rotate(150deg); +} + +html[theme="orange"] .image-brand { + filter: hue-rotate(-160deg); +} + +.v0ck_overlay { + background-color: none !important; +} + +.tags { + display: grid; +} + +@media (min-width: 600px) { + .tags { grid-template-columns: repeat(2, 1fr); } +} + +@media (min-width: 900px) { + .tags { grid-template-columns: repeat(3, 1fr); } +} + +.tag { + box-shadow: 1px 1px 1px black; + display: grid; + margin: 5px; + grid-template-columns: auto; + grid-template-rows: 1fr; +} + +.navigation-rechts { + display: flex; +} + +.about { + background: none!important; +} + +.login-image { + width: 300px; +} + +.tos { + margin: 2em; +} + +.nav-link[data-toggle="dropdown"]::after { + content: "" !important; +} + +.dropdown-menu.shii { + width: auto; +} + +@media (min-width: 900px) { + .tags { + grid-template-columns: repeat(auto-fill, 20em); + justify-content: center; + } +} + +.tag img { + width: 100%; +} + +@media (max-width: 1056px) { + html, body { + text-align: left; + } +} \ No newline at end of file diff --git a/public/s/img/404.gif b/public/s/img/404.gif new file mode 100644 index 0000000..045b2ae Binary files /dev/null and b/public/s/img/404.gif differ diff --git a/public/s/img/cockfag.png b/public/s/img/cockfag.png new file mode 100644 index 0000000..e88f201 Binary files /dev/null and b/public/s/img/cockfag.png differ diff --git a/public/s/img/favicon.png b/public/s/img/favicon.png index 4957923..b6a8ce4 100644 Binary files a/public/s/img/favicon.png and b/public/s/img/favicon.png differ diff --git a/public/s/img/w0bm_mosh_banner_by_marderchen.gif b/public/s/img/w0bm_mosh_banner_by_marderchen.gif new file mode 100644 index 0000000..b78c4f1 Binary files /dev/null and b/public/s/img/w0bm_mosh_banner_by_marderchen.gif differ diff --git a/public/s/js/admin.js b/public/s/js/admin.js index 6e1c9ff..c5c0d0c 100644 --- a/public/s/js/admin.js +++ b/public/s/js/admin.js @@ -1,41 +1,3 @@ -let flashActive = false; -const flashTypes = [ "error", "success", "warn" ]; - -const flash = ({ type, msg }) => { - let flashContainer; - if(tmp = document.querySelector("div#flash")) - flashContainer = tmp; - else { - flashContainer = document.createElement("div"); - flashContainer.id = "flash"; - document.body.insertAdjacentElement("afterbegin", flashContainer); - } - - flashContainer.innerHTML = msg; - if(flashTypes.includes(type)) { - flashContainer.className = ""; - flashContainer.classList.add(type); - } - if(flashActive) - return false; - - flashActive = true; - flashContainer.animate( - [ { bottom: "-28px" }, { bottom: 0 } ], { - duration: 500, - fill: "both" - } - ).onfinish = () => setTimeout(() => { - flashContainer.animate( - [ { bottom: 0 }, { bottom: "-28px" } ], { - duration: 500, - fill: "both" - } - ).onfinish = () => flashActive = false; - }, 4 * 1e3); - return true; -}; - (async () => { if(_addtag = document.querySelector("a#a_addtag")) { const postid = +document.querySelector("a.id-link").innerText; @@ -140,10 +102,8 @@ const flash = ({ type, msg }) => { tagname: tmptag }); if(!res.success) { - return flash({ - type: "error", - msg: res.msg - }); + alert(res.msg); + return false; } tags = res.tags.map(t => t.tag); renderTags(res.tags); @@ -202,12 +162,6 @@ const flash = ({ type, msg }) => { })).json(); renderTags(res.tags); - tags = res.tags.map(t => t.tag); - - flash({ - type: "success", - msg: tags.join() - }); }; const deleteButtonEvent = async e => { @@ -218,17 +172,8 @@ const flash = ({ type, msg }) => { const res = await post("/api/v2/admin/deletepost", { postid: postid }); - if(res.success) { - flash({ - type: "success", - msg: "post was successfully deleted" - }); - } - else { - flash({ - type: "error", - msg: res.msg - }); + if(!res.success) { + alert(res.msg); } }; @@ -338,7 +283,7 @@ const flash = ({ type, msg }) => { document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent); document.addEventListener("keyup", e => { - if(e.target.tagName === "INPUT") + if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; if(e.key === "p") toggleEvent(); diff --git a/public/s/js/f0ck.js b/public/s/js/f0ck.js index 94e3bd8..07894b6 100644 --- a/public/s/js/f0ck.js +++ b/public/s/js/f0ck.js @@ -11,7 +11,7 @@ window.requestAnimFrame = (function(){ if(elem = document.querySelector("#my-video")) { video = new v0ck(elem); document.addEventListener("keydown", e => { - if(e.key === " " && e.target.tagName !== "INPUT") { + if(e.key === " " && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") { video[video.paused ? 'play' : 'pause'](); document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden'); } @@ -72,7 +72,7 @@ window.requestAnimFrame = (function(){ " ": clickOnElementBinding("#f0ck-image") }; document.addEventListener("keydown", e => { - if(e.key in keybindings && e.target.tagName !== "INPUT") { + if(e.key in keybindings && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") { if(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return; e.preventDefault(); diff --git a/public/s/js/theme.js b/public/s/js/theme.js index 9a6368e..7cd37b9 100644 --- a/public/s/js/theme.js +++ b/public/s/js/theme.js @@ -15,7 +15,7 @@ const Cookie = { }; (() => { - const acttheme = Cookie.get('theme') ?? "f0ck"; + const acttheme = Cookie.get('theme') ?? "w0bm"; const themecontainer = document.querySelector("li#themes > ul.dropdown-menu"); const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); if(acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme)) @@ -30,9 +30,9 @@ const Cookie = { })); document.addEventListener("keydown", e => { - if(e.target.tagName === "INPUT") + if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; - const acttheme = Cookie.get('theme') ?? "f0ck"; + const acttheme = Cookie.get('theme') ?? "w0bm"; const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); const k = e.key; if(k === "t") { diff --git a/public/s/js/user.js b/public/s/js/user.js new file mode 100644 index 0000000..0c5e963 --- /dev/null +++ b/public/s/js/user.js @@ -0,0 +1,229 @@ +(async () => { + if(_addtag = document.querySelector("a#a_addtag")) { + const postid = +document.querySelector("a.id-link").innerText; + const poster = document.querySelector("a#a_username").innerText; + let tags = [...document.querySelectorAll("#tags > .badge")].map(t => t.innerText.slice(0, -2)); + + const queryapi = async (url, data, method = 'GET') => { + let req; + if(method == 'POST') { + req = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }); + } + else { + let s = []; + for(const [ key, val ] of Object.entries(data)) + s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val)); + req = await fetch(url + '?' + s.join('&')); + } + return await req.json(); + }; + + const get = async (url, data) => queryapi(url, data, 'GET'); + const post = async (url, data) => queryapi(url, data, 'POST'); + + const renderTags = _tags => { + [...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag)); + _tags.reverse().forEach(tag => { + const a = document.createElement("a"); + a.href = `/tag/${tag.normalized}`; + a.style = "color: inherit !important"; + a.innerHTML = tag.tag; + + const span = document.createElement("span"); + span.classList.add("badge", "mr-2"); + span.setAttribute('tooltip', tag.user); + + tag.badge.split(" ").forEach(b => span.classList.add(b)); + + span.insertAdjacentElement("beforeend", a); + + document.querySelector("#tags").insertAdjacentElement("afterbegin", span); + }); + }; + + const addtagClick = (ae = false) => { + if(ae) + ae.preventDefault(); + + const insert = document.querySelector("a#a_addtag"); + const span = document.createElement("span"); + span.classList.add("badge", "badge-light", "mr-2"); + + const input = document.createElement("input"); + input.size = "10"; + input.value = ""; + input.setAttribute("list", "testlist"); + input.setAttribute("autoComplete", "off"); + + span.insertAdjacentElement("afterbegin", input); + insert.insertAdjacentElement("beforebegin", span); + + input.focus(); + + let tt = null; + let lastInput = ''; + const testList = document.querySelector('#testlist'); + + input.addEventListener("keyup", async e => { + if(e.key === "Enter") { + const tmptag = input.value?.trim(); + if(tags.includes(tmptag)) + return alert("tag already exists"); + const res = await post("/api/v2/admin/" + postid + "/tags", { + tagname: tmptag + }); + if(!res.success) { + alert(res.msg); + return false; + } + tags = res.tags.map(t => t.tag); + renderTags(res.tags); + addtagClick(); + testList.innerText = ""; + } + else if(e.key === "Escape") { + span.parentElement.removeChild(span); + testList.innerText = ""; + } + else { + if(tt != null) + clearTimeout(tt); + + tt = setTimeout(async () => { + tt = null; + + const tmptag = input.value?.trim(); + + if(tmptag == lastInput || tmptag.length <= 1) + return false; + + testList.innerText = ""; + lastInput = tmptag; + + const res = await get('/api/v2/admin/tags/suggest', { + q: tmptag + }); + + for(const entry of res.suggestions) { + const option = document.createElement('option'); + option.value = entry.tag; + + if(!/fox/.test(navigator.userAgent)) + option.innerText = `tagged ${entry.tagged} times (score: ${entry.score.toFixed(2)})`; + + testList.insertAdjacentElement('beforeEnd', option); + }; + }, 500); + } + return true; + }); + + input.addEventListener("focusout", ie => { + if(input.value.length === 0) + input.parentElement.parentElement.removeChild(input.parentElement); + }); + }; + + const toggleEvent = async (e = false) => { + if(e) + e.preventDefault(); + + const res = await (await fetch('/api/v2/admin/' + encodeURIComponent(postid) + '/tags/toggle', { + method: 'PUT' + })).json(); + + renderTags(res.tags); + }; + + const toggleFavEvent = async e => { + const res = await post('/api/v2/admin/togglefav', { + postid: postid + }); + if(res.success) { + const fav = document.querySelector("svg#a_favo > use").href; + fav.baseVal = '/s/img/iconset.svg#heart_' + (fav.baseVal.match(/heart_(regular|solid)$/)[1] == "solid" ? "regular" : "solid"); + + // span#favs + const favcontainer = document.querySelector('span#favs'); + favcontainer.innerHTML = ""; + + favcontainer.hidden = !(favcontainer.hidden || res.favs.length > 0); + + res.favs.forEach(f => { + const a = document.createElement('a'); + a.href = `/user/${f.user}/favs`; + a.setAttribute('tooltip', f.user); + a.setAttribute('flow', 'up'); + + const img = document.createElement('img'); + img.src = `/t/${f.avatar}.webp`; + img.style.height = "32px"; + img.style.width = "32px"; + + a.insertAdjacentElement('beforeend', img); + favcontainer.insertAdjacentElement('beforeend', a); + favcontainer.innerHTML += " "; + }); + } + else { + // lul + } + }; + + _addtag.addEventListener("click", addtagClick); + document.querySelector("a#a_toggle").addEventListener("click", toggleEvent); + document.querySelector("svg#a_favo").addEventListener("click", toggleFavEvent); + + document.addEventListener("keyup", e => { + if(e.target.tagName === "INPUT") + return; + if(e.key === "p") + toggleEvent(); + else if(e.key === "i") + addtagClick(); + else if(e.key === "x") + deleteButtonEvent(); + else if(e.key === "f") + toggleFavEvent(); + }); + } + + if(document.location.pathname === '/settings') { + const saveAvatar = async e => { + e.preventDefault(); + + const avatar = +document.querySelector('input[name="i_avatar"]').value; + let res = await fetch('/api/v2/settings/setAvatar', { + method: 'PUT', + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ avatar }) + }); + const code = res.status; + res = await res.json(); + + switch(code) { + case 200: + document.querySelector('#img_avatar').src = `/t/${avatar}.webp`; + document.querySelector('img.avatar').src = `/t/${avatar}.webp`; + break; + default: + console.log(res); + break; + } + }; + + document.querySelector('input#s_avatar').addEventListener('click', saveAvatar); + document.querySelector('input[name="i_avatar"]').addEventListener('keyup', async e => { + if(e.key === 'Enter') + await saveAvatar(e); + }); + } +})(); diff --git a/src/inc/lib.mjs b/src/inc/lib.mjs index 45aa0d2..6bb81d4 100644 --- a/src/inc/lib.mjs +++ b/src/inc/lib.mjs @@ -221,14 +221,14 @@ export default new class { return next(); }; - async loggedin(req, res, next) { - if(!req.session) { - return res.reply({ - code: 401, - body: "401 - Unauthorized" - }); - } - return next(); + async loggedin(req, res, next) { + if(!req.session) { + return res.reply({ + code: 401, + body: "401 - Unauthorized" + }); + } + return next(); }; }; diff --git a/src/inc/queue.mjs b/src/inc/queue.mjs index 8f0f2db..0b255b3 100644 --- a/src/inc/queue.mjs +++ b/src/inc/queue.mjs @@ -62,7 +62,7 @@ export default new class queue { if(mime.startsWith('video/') || mime == 'image/gif') await this.exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`); else if(mime.startsWith('image/') && mime != 'image/gif') - await this.exec(`convert "./public/b/${filename}[0]" ./tmp/${itemid}.png`); + await this.exec(`magick "./public/b/${filename}[0]" ./tmp/${itemid}.png`); else if(mime.startsWith('audio/')) { if(link.match(/soundcloud/)) { let cover = (await this.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --get-thumbnail "${link}"`)).stdout.trim(); @@ -72,23 +72,23 @@ export default new class queue { await this.exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`); const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size; if(size >= 0) { - await this.exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`); - await this.exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`); + await this.exec(`magick ./tmp/${itemid}.jpg ./tmp/${itemid}.png`); + await this.exec(`magick ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`); } } catch(err) {} } else { await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`); - await this.exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); + await this.exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); } } else { await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`); - await this.exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); + await this.exec(`magick ./tmp/${itemid}.png ./public/ca/${itemid}.webp`); } } - await this.exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`); + await this.exec(`magick "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`); await fs.promises.unlink(`./tmp/${itemid}.png`).catch(_=>{}); await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(_=>{}); return true; diff --git a/src/inc/routeinc/f0cklib.mjs b/src/inc/routeinc/f0cklib.mjs index aabab30..a7786f9 100644 --- a/src/inc/routeinc/f0cklib.mjs +++ b/src/inc/routeinc/f0cklib.mjs @@ -166,7 +166,7 @@ export default { success: true, user: { name: actitem.username, - channel: actitem.usernetwork == "Telegram" && actitem.userchannel !== "f0ck.me" ? "anonymous" : actitem.userchannel, + channel: actitem.usernetwork == "Telegram" && actitem.userchannel !== "w0bm.com" ? "anonymous" : actitem.userchannel, network: actitem.usernetwork }, item: { @@ -187,7 +187,7 @@ export default { favorites: favorites, tags: tags }, - title: `${actitem.id} - f0ck.me`, + title: `${actitem.id} - w0bm.com`, pagination: { end: items[items.length - 1]?.id, start: items[0]?.id, @@ -201,49 +201,65 @@ export default { tmp }; return data; - }, - getRandom: async (o = ({ user, tag, mime, mode, session })) => { - const user = o.user ? decodeURI(o.user) : null; - const tag = lib.parseTag(o.tag ?? null); - const mime = (o.mime ?? ""); - const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%"; - + },getRandom: async (o = ({ user, tag, mime, mode, fav, session })) => { + const user = o.user ? decodeURI(o.user) : null; + const tag = lib.parseTag(o.tag ?? null); + const mime = (o.mime ?? ""); + const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%"; + const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0); - - const item = await db` - select - items.id - from items - left join tags_assign on tags_assign.item_id = items.id - left join tags on tags.id = tags_assign.tag_id - left join favorites on favorites.item_id = items.id - left join "user" on "user".id = favorites.user_id - where - ${ db.unsafe(modequery) } - and items.active = 'true' - ${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` } - ${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` } - ${ user ? db`and items.username ilike ${'%'+user+'%'}` : db`` } - ${ mime ? db`and items.mime ilike ${smime}` : db`` } - ${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` } - group by items.id, tags.tag - order by random() - limit 1 - `; - - if(item.length === 0) { + + let item; + + if (o.fav && user) { + // Special case: random from user's favorites + item = await db` + select + items.id + from favorites + inner join items on favorites.item_id = items.id + inner join "user" on "user".id = favorites.user_id + where + "user".user ilike ${'%' + user + '%'} + and items.active = 'true' + ${mime ? db`and items.mime ilike ${smime}` : db``} + order by random() + limit 1 + `; + } else { + // Normal random logic + item = await db` + select + items.id + from items + left join tags_assign on tags_assign.item_id = items.id + left join tags on tags.id = tags_assign.tag_id + where + ${db.unsafe(modequery)} + and items.active = 'true' + ${tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db``} + ${user ? db`and items.username ilike ${'%' + user + '%'}` : db``} + ${mime ? db`and items.mime ilike ${smime}` : db``} + ${!o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db``} + group by items.id, tags.tag + order by random() + limit 1 + `; + } + + if (item.length === 0) { return { success: false, message: "no f0cks found :(" }; } - + const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks' }); - + return { success: true, link: link, itemid: item[0].id }; } -}; + }; diff --git a/src/inc/routes/admin.mjs b/src/inc/routes/admin.mjs index 60ad38a..af1f67a 100644 --- a/src/inc/routes/admin.mjs +++ b/src/inc/routes/admin.mjs @@ -121,77 +121,79 @@ export default (router, tpl) => { }); }); - router.get(/^\/admin\/log(\/)?$/, lib.auth, async (req, res) => { - exec("journalctl -qeu f0ck --no-pager", (err, stdout) => { - res.reply({ - body: tpl.render("admin/log", { - log: stdout.split("\n").slice(0, -1), - tmp: null - }, req) - }); - }); - }); + // router.get(/^\/admin\/log(\/)?$/, lib.auth, async (req, res) => { + // // Funktioniert ohne systemd service natürlich nicht. + // exec("journalctl -qeu f0ck --no-pager", (err, stdout) => { + // res.reply({ + // body: tpl.render("admin/log", { + // log: stdout.split("\n").slice(0, -1), + // tmp: null + // }, req) + // }); + // }); + // }); - router.get(/^\/admin\/recover\/?/, lib.auth, async (req, res) => { - if(req.url.qs?.id) { - const id = +req.url.qs.id; - const f0ck = await db` - select dest, mime - from "items" - where - id = ${id} and - active = 'false' - limit 1 - `; - if(f0ck.length === 0) { - return res.reply({ - body: `f0ck ${id}: f0ck not found` - }); - } + // router.get(/^\/admin\/recover\/?/, lib.auth, async (req, res) => { + // Gelöschte Objekte werden nicht aufgehoben. + // if(req.url.qs?.id) { + // const id = +req.url.qs.id; + // const f0ck = await db` + // select dest, mime + // from "items" + // where + // id = ${id} and + // active = 'false' + // limit 1 + // `; + // if(f0ck.length === 0) { + // return res.reply({ + // body: `f0ck ${id}: f0ck not found` + // }); + // } - await db`update "items" set active = 'true' where id = ${id}`; + // await db`update "items" set active = 'true' where id = ${id}`; - await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{}); - await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{}); - await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{}); - await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{}); + // await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{}); + // await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{}); + // await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{}); + // await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{}); - if(f0ck[0].mime.startsWith('audio')) { - await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{}); - await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{}); - } + // if(f0ck[0].mime.startsWith('audio')) { + // await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{}); + // await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{}); + // } - return res.reply({ - body: `f0ck ${id} recovered. back` - }); - } + // return res.reply({ + // body: `f0ck ${id} recovered. back` + // }); + // } - const _posts = await db` - select id, mime, username - from "items" - where - active = 'false' - order by id desc - `; + // const _posts = await db` + // select id, mime, username + // from "items" + // where + // active = 'false' + // order by id desc + // `; - if(_posts.length === 0) { - return res.reply({ - body: 'blah' - }); - } + // if(_posts.length === 0) { + // return res.reply({ + // body: 'blah' + // }); + // } - const posts = await Promise.all(_posts.map(async p => ({ - ...p, - thumbnail: (await fs.readFile(`./deleted/t/${p.id}.webp`)).toString('base64') - }))); + // const posts = await Promise.all(_posts.map(async p => ({ + // ...p, + // thumbnail: (await fs.readFile(`./deleted/t/${p.id}.webp`)).toString('base64') + // }))); - res.reply({ - body: tpl.render('admin/recover', { - posts, - tmp: null - }, req) - }); - }); + // res.reply({ + // body: tpl.render('admin/recover', { + // posts, + // tmp: null + // }, req) + // }); + // }); return router; }; diff --git a/src/inc/routes/apiv2/tags.mjs b/src/inc/routes/apiv2/tags.mjs index fa0d4b3..7a2df34 100644 --- a/src/inc/routes/apiv2/tags.mjs +++ b/src/inc/routes/apiv2/tags.mjs @@ -131,13 +131,13 @@ export default router => { const postid = +req.params.postid; const tagname = decodeURIComponent(req.params.tagname); - if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') { - return res.json({ - success: false, - msg: 'blacklisted', - tags: await lib.getTags(postid) - }); - } + // if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') { + // return res.json({ + // success: false, + // msg: 'blacklisted', + // tags: await lib.getTags(postid) + // }); + // } const tags = await lib.getTags(postid); diff --git a/src/inc/routes/index.mjs b/src/inc/routes/index.mjs index 9d936dc..20aca11 100644 --- a/src/inc/routes/index.mjs +++ b/src/inc/routes/index.mjs @@ -111,6 +111,12 @@ export default (router, tpl) => { }); }); + router.get(/^\/(terms)$/, (req, res) => { + res.reply({ + body: tpl.render(req.url.split[0], { tmp: null }, req) + }); + }); + router.get(/^\/mode\/(\d)/, lib.loggedin, async (req, res) => { const mode = +req.url.split[1]; let referertmp = req.headers.referer; diff --git a/src/inc/routes/random.mjs b/src/inc/routes/random.mjs index 6af00c1..49101a8 100644 --- a/src/inc/routes/random.mjs +++ b/src/inc/routes/random.mjs @@ -6,10 +6,14 @@ export default (router, tpl) => { let referer = req.headers.referer ?? ''; let opts = {}; - if(referer.match(new RegExp(cfg.main.url.regex))) { // parse referer + if (referer.match(new RegExp(cfg.main.url.regex))) { // parse referer referer = referer.split(cfg.main.url.domain)[1]; + console.log("referer: ", referer); + const tmp = referer.match(/^\/?(?:\/tag\/(?.+?))?(?:\/user\/(?.+?)\/(?f0cks|favs))?(?:\/(?image|audio|video))?(?:\/p\/(?\d+))?(?:\/(?\d+))?$/); - if(tmp) + console.log("tmp: ", tmp); + + if (tmp && tmp.groups) opts = tmp.groups; } @@ -18,12 +22,14 @@ export default (router, tpl) => { tag: opts.tag, mime: opts.mime, page: opts.page, - fav: opts.mode == 'favs', + fav: opts.mode === 'favs', mode: req.session.mode, session: !!req.session }); - if(!data.success) { + console.log("data", data); + + if (!data.success) { return res.reply({ code: 404, body: tpl.render('error', { @@ -33,7 +39,8 @@ export default (router, tpl) => { }); } - res.redirect(`${data.link.main}${data.link.path}${data.itemid}`); + res.redirect(encodeURI(`${data.link.main}${data.link.path}${data.itemid}`)); }); + return router; -}; +}; \ No newline at end of file diff --git a/src/inc/routes/toptags.mjs b/src/inc/routes/toptags.mjs new file mode 100644 index 0000000..7f4252d --- /dev/null +++ b/src/inc/routes/toptags.mjs @@ -0,0 +1,42 @@ +import db from "../../inc/sql.mjs"; +import cfg from "../../inc/config.mjs"; + +export default (router, tpl) => { + router.get(/^\/tags$/, async (req, res) => { + + const phrase = cfg.websrv.phrases[~~(Math.random() * cfg.websrv.phrases.length)]; + + const nsfp = cfg.nsfp.map(n => `${n}`); + + const toptags = await db` + SELECT t.id, t.tag, COUNT(DISTINCT ta.item_id) AS total_items + FROM tags t + LEFT JOIN tags_assign ta ON t.id = ta.tag_id + WHERE t.id not in (${db.unsafe(nsfp)}) + GROUP BY t.id, t.tag + ORDER BY total_items DESC + LIMIT 500 + ; + `; + + const toptags_regged = await db` + SELECT t.id, t.tag, COUNT(DISTINCT ta.item_id) AS total_items + FROM tags t + LEFT JOIN tags_assign ta ON t.id = ta.tag_id + GROUP BY t.id, t.tag + ORDER BY total_items DESC + LIMIT 500 + ; + `; + + res.reply({ + body: tpl.render('tags', { + toptags, + toptags_regged, + phrase, + tmp: null + }, req) + }); + }); + return router; +}; diff --git a/src/inc/trigger/f0ckgag.mjs b/src/inc/trigger/f0ckgag.mjs index 8265b2b..e894c7c 100644 --- a/src/inc/trigger/f0ckgag.mjs +++ b/src/inc/trigger/f0ckgag.mjs @@ -2,7 +2,7 @@ import cfg from "../config.mjs"; import db from "../sql.mjs"; import lib from "../lib.mjs"; -const regex = /(https\:\/\/f0ck\.me|http\:\/\/fockmoonsb24iczs7odozzy5uktlzbcgp337nabrgffzxv5ihabgpvyd\.onion)(\/(video|image|audio))?\/(\d+|(?:b\/)(\w{8})\.(jpg|webm|gif|mp4|png|mov|mp3|ogg|flac))/gi; +const regex = /(https\:\/\/w0bm\.com)(\/(video|image|audio))?\/(\d+|(?:b\/)(\w{8})\.(jpg|webm|gif|mp4|png|mov|mp3|ogg|flac))/gi; export default async bot => { diff --git a/src/inc/trigger/parser.mjs b/src/inc/trigger/parser.mjs index 10d22c2..36fc781 100644 --- a/src/inc/trigger/parser.mjs +++ b/src/inc/trigger/parser.mjs @@ -34,7 +34,7 @@ export default async bot => { if(e.message.match(/\!i(gnore)?\b/)) return false; - if(!e.channel.includes("f0ck") && (!e.message.match(/\!f(0ck)?\b/i) && (typeof e.raw.forward_from == 'undefined'))) + if(!e.channel.includes("f0bm") && (!e.message.match(/\!f(0ck)?\b/i) && (typeof e.raw.forward_from == 'undefined'))) return false; if(e.type === 'tg' && // proto: tg @@ -138,7 +138,7 @@ export default async bot => { console.error('err:', err); if(e.type == 'tg') return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err); - return await e.reply('something went wrong lol'); + return await e.reply('something went wrong lol / check maxfilesize?'); } } // @@ -247,7 +247,7 @@ export default async bot => { try { await queue.genThumbnail(filename, mime, itemid, link); } catch(err) { - await queue.exec(`convert ./mugge.png ./public/t/${itemid}.webp`); + await queue.exec(`magick ./mugge.png ./public/t/${itemid}.webp`); } let speed = lib.calcSpeed(size, end); diff --git a/src/index.mjs b/src/index.mjs index 810178b..ceda4a9 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -66,7 +66,7 @@ process.on('unhandledRejection', err => { req.session = false; if(req.url.pathname.match(/^\/(s|b|t|ca)\//)) return; - req.theme = req.cookies.theme || 'f0ck'; + req.theme = req.cookies.theme || 'atmos'; req.fullscreen = req.cookies.fullscreen || 0; if(req.cookies.session) { @@ -110,7 +110,7 @@ process.on('unhandledRejection', err => { db({ user_id: +user[0].id, mode: user[0].mode ?? 0, - theme: req.session.theme ?? 'f0ck', + theme: req.session.theme ?? 'atmos', fullscreen: req.session.fullscreen || 0 }, 'user_id', 'mode', 'theme', 'fullscreen') } diff --git a/views/about.html b/views/about.html index 1108042..830a858 100644 --- a/views/about.html +++ b/views/about.html @@ -1,51 +1,13 @@ @include(snippets/header)
-
-
- -

thanks to our turkish fellow lol@n0xy/#f0ck for this gif <3

-
-
f0ck Contact
-

Whatever it is, we might have a answer, even though it might not be the one you were looking for: admin@f0ck.me

-
About f0ck
-

f0ck is your friendly IRC shitposting bot, it's built for catching urls that are passed to it and displays the content of passed urls on a simple and accessible web gallery reachable at f0ck.me

-
WTF is a f0ck?
-

A f0ck is basically giving a fuck about some internet bullshit, like stupid images, videos and so on, but also for great things like good music taste and shit, it's basically "a f0ck was given" and f0ck and it's users gave a lot of f0cks over the past years, it's not hard to finally start giving a damn f0ck about something, just f0ck it dood!

-
Where to f0ck?
-

#f0ck on n0xy.net

-

You can invite f0ck to your channel on the following supported networks by simply typing
/invite f0ck

- -

To start f0cking the shit out of something simply add a !f0ck behind the url you want to f0ck, that's it

-

#f0ck specific: to have f0ck ignore a link add !ignore at the end
Example: https://www.youtube.com/watch?v=dQw4w9WgXcQ !ignore

-

f0ck supports a variety of websites, in fact all websites supported by yt-dlp are supported by f0ck aswell!

-
f0ck Rules
-
    -
  • You must be 18 years or older to visit or post
  • -
  • You shall not post animal cruelty, we like our animals alive and well, living a happy life until they are ready for our Schnitzel!
  • -
  • You shall not post under ANY circumstances: Snuff, Beastiality, Rape, Terrorist stuff (Beheadings, First person shootings, warcrimes), Childporn, Childmodeling
  • -
-
f0cked up?
-

To have something removed in case you accidentally f0cked something that actually shouldn't be f0cked you can always contact the admins either via IRC or Email

-
    -
  • irc.n0xy.net #f0ck
  • -
  • admin@f0ck.me
  • -
-
Compatibility
-

f0ck is developed and tested for Firefox and Chromium in their latest versions

-

If you encounter bugs please report them so we can fix them.

-

Microsoft Edgy is not actively supported, but if it werks, great! Same for anything apple related.

-
Tinfoil f0ckers listen!
-

f0ck onions and moons, but fockulite!

-

http://fockmoonsb24iczs7odozzy5uktlzbcgp337nabrgffzxv5ihabgpvyd.onion

-

http://fockulite74atso2xsxxw6q2gzqrgck572tiwvkyf5vdxictjn2vmlyd.onion

-

f0ck is completely functional without javascript enabled, you can be the beardiest neckbeard of all, we got you m'gentleman, if you want to use a custom theme you gotta allow our style cookie.

-
f0ck Privacy?
-

Cookies: Yes, we set 1 cookie for your prefered stylesheet, this is a optional cookie and not required for the site to function, simply cosmetics, you can block this cookie and the site will still work as intended and will default to the default f0ck theme called "f0ck"

-

Logs: We do not log users accessing our website.

-
+
+

Welcome stranger!

+

bringing you some of the greatest webms from the past, the present and the future!

+

Enjoy your stay.

+ cockfag +

If you have any questions you can reach out via Mail.

+

mail: admin@w0bm.com

+

Please also make yourself familiar with the Terms Of Service

+
@include(snippets/footer) diff --git a/views/admin.html b/views/admin.html index a144f8e..568a498 100644 --- a/views/admin.html +++ b/views/admin.html @@ -12,8 +12,8 @@

Adminwerkzeuge

diff --git a/views/item.html b/views/item.html index 318d22f..5f8d230 100644 --- a/views/item.html +++ b/views/item.html @@ -2,14 +2,11 @@
+
-
- -
-
- f0ck - {{ item.id }} -
+
+
@if(session)
@@ -36,7 +33,7 @@
@elseif(item.mime.startsWith("audio")) -
+
@elseif(item.mime.startsWith("image")) @@ -62,23 +59,39 @@
-
@include(snippets/footer) diff --git a/views/login.html b/views/login.html index 5cda379..dd57f06 100644 --- a/views/login.html +++ b/views/login.html @@ -3,12 +3,12 @@ - f0ck login + login