3 Commits
master ... dev

Author SHA1 Message Date
7028d92d5e changing convert to magick command because of deprecation
All checks were successful
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2024-07-04 03:08:59 +02:00
c1f06de7c5 fixing p1nk theme nav-bg color
All checks were successful
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2024-07-04 01:56:14 +02:00
ce7f854dd6 wip
All checks were successful
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2024-07-04 01:18:41 +02:00
24 changed files with 205 additions and 695 deletions

View File

@ -5,7 +5,6 @@
"domain": "f0ck.dev",
"regex": "f0ck\\.dev"
},
"socks": "",
"maxfilesize": 83886080,
"adminmultiplier": 3.5,
"ignored": [

View File

@ -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) {}

View File

@ -422,6 +422,7 @@ CREATE TABLE public.user_sessions (
browser character varying(255) NOT NULL,
created_at integer NOT NULL,
last_used integer NOT NULL,
last_action character varying(255) NOT NULL,
kmsi smallint DEFAULT '0'::smallint NOT NULL
);

133
package-lock.json generated
View File

@ -12,7 +12,6 @@
"cuffeo": "^1.2.2",
"flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5",
"node-fetch": "^3.3.2",
"postgres": "^3.3.4"
}
},
@ -24,38 +23,6 @@
"flumm-fetch": "^1.0.1"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/flumm-fetch": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
@ -66,55 +33,6 @@
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/postgres": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
@ -123,15 +41,6 @@
"type": "individual",
"url": "https://github.com/sponsors/porsager"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
}
},
"dependencies": {
@ -143,20 +52,6 @@
"flumm-fetch": "^1.0.1"
}
},
"data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
},
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"flumm-fetch": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
@ -167,38 +62,10 @@
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
},
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"requires": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}
},
"postgres": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
"integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg=="
},
"web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="
}
}
}

View File

@ -18,7 +18,6 @@
"cuffeo": "^1.2.2",
"flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5",
"node-fetch": "^3.3.2",
"postgres": "^3.3.4"
}
}

View File

@ -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;
@ -463,10 +463,6 @@ html[theme="paper"] .login-form button[type="submit"] {
color: black;
}
html[theme="paper"] .btn.disabled {
text-shadow: 1px 1px 3px;
}
html[theme="atmos"] {
--accent: #1fb2b0;
--bg: #161618;
@ -585,6 +581,10 @@ html[theme="term"] span#favs {
border: 1px solid var(--black);
}
html[theme="term"] .metadata {
text-shadow: 0px 0px 1px var(--accent), 1px 0px 2px var(--accent), 0px 0px 5px var(--black);
}
html[theme="term"] .metadata>.badge-dark,
#themeselector {
box-shadow: var(--pagination-anchor-box-shadow);
@ -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,
@ -1850,12 +1851,11 @@ span.placeholder {
}
}
/* foobar loooool sooooooooooos */
/* @media (min-width: 1200px) {
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
} */
}
.embed-responsive-16by9::before {
padding-top: 50%;
@ -2402,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 {
@ -2415,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 */
@ -3017,146 +3017,7 @@ input#s_avatar {
#s_avatar:hover {
background: #ffffff0f;
}
/* nav ↓ (mobile) */
@media (max-width: 768px) {
body {
.theforceofthree {
display: grid;
grid-template-rows: 1fr auto;
height: 100%;
}
.navbar.navbar-expand-lg {
grid-row-start: 2;
position: sticky;
bottom: 0;
}
.nav-link .user span {
visibility: hidden;
}
.navbar-nav {
display: grid !important;
grid-template-rows: 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 0.25em;
}
.dropdown-menu {
position: absolute;
bottom: 30px;
top: unset;
}
.navbar-brand {
line-height: 1.5;
}
.navUserName {
display: none;
}
.container {
padding-top: 15px;
padding-bottom: 3em;
padding-left: 15px;
padding-right: 15px;
}
html {
height: 100%;
}
.err {
margin: unset;
width: unset;
}
.dropdown-menu {
list-style: none;
padding: 0;
min-width: 25vw;
border-left: 1px solid;
border-right: 1px solid;
border-bottom: 1px solid;
border-top: 1px solid;
border-color: var(--black);
margin-bottom: 0;
text-align: left;
box-shadow: 2px 3px 5px var(--black);
}
.dropdown-menu li {
padding-left: 6px;
}
.nav-link {
border-radius: 0;
}
.metadata {
grid-template-columns: auto;
}
button[data-skip="-10"] {
display: none;
}
button[data-skip="10"] {
display: none;
}
.v0ck_player_button {
font-family: monospace;
font-size: 12px;
}
tr {
display: grid;
grid-template-columns: auto 1fr;
}
tbody td {
box-shadow: none;
}
table tr td:nth-child(4) {
color: yellow;
font-size: xx-large;
font-family: 'VCR';
justify-content: center;
align-items: center;
align-self: center;
}
td:nth-child(1) {
grid-column: 1;
}
td:nth-child(2) {
grid-column: 2;
grid-row: 2;
}
td:nth-child(3) {
grid-column: 2;
grid-row: 1;
}
td:nth-child(4) {
grid-column: 3;
}
}
.gay-button {
width: fit-content;
margin: 0 auto;
}
/* f0ck ultra smalls */
@media (min-width: 1800px) {
.container {
max-width: 50%;
}
grid-template-columns: 0.4fr 1fr 0.4fr;
}

View File

@ -1,3 +1,41 @@
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;
@ -102,8 +140,10 @@
tagname: tmptag
});
if(!res.success) {
alert(res.msg);
return false;
return flash({
type: "error",
msg: res.msg
});
}
tags = res.tags.map(t => t.tag);
renderTags(res.tags);
@ -162,6 +202,12 @@
})).json();
renderTags(res.tags);
tags = res.tags.map(t => t.tag);
flash({
type: "success",
msg: tags.join()
});
};
const deleteButtonEvent = async e => {
@ -172,8 +218,17 @@
const res = await post("/api/v2/admin/deletepost", {
postid: postid
});
if(!res.success) {
alert(res.msg);
if(res.success) {
flash({
type: "success",
msg: "post was successfully deleted"
});
}
else {
flash({
type: "error",
msg: res.msg
});
}
};

View File

@ -219,6 +219,16 @@ window.requestAnimFrame = (function(){
elem = document.querySelector(".pagination > .prev:not(.disabled)");
}
}
else {
if(Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
if(navbar = document.querySelector("nav.navbar") && document.querySelector("div.posts")) {
if(swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
elem = document.querySelector(".pagination > .next:not(.disabled)");
else if(swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div.posts")) // down
elem = document.querySelector(".pagination > .prev:not(.disabled)");
}
}
}
swipeRT.xDown = null;
swipeRT.yDown = null;

View File

@ -1,229 +0,0 @@
(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 += "&nbsp;";
});
}
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);
});
}
})();

View File

@ -149,15 +149,7 @@ class v0ck {
player.addEventListener('click', e => {
const path = e.path || (e.composedPath && e.composedPath());
if(path.filter(f => f.classList?.contains('v0ck_player_controls')).length)
return;
//navigator.userAgentData = { mobile: true };
if(navigator.userAgentData?.mobile) { // mobile
return;
}
if(!path.filter(f => f.classList?.contains('v0ck_player_controls')).length)
togglePlay(e);
});
toggle.addEventListener('click', togglePlay);

View File

@ -1,13 +1,6 @@
import fetch from 'node-fetch';
import fetch from 'flumm-fetch';
import cfg from './config.mjs';
const parseOutput = text => {
text = text.trim().toLowerCase();
return text.includes(', ')
? text.split(', ')
: text.replace(/\d\. /g, '').split('\n');
};
export default new class autotagger {
async isNSFW(filename, filesize) {
let opts = {
@ -36,34 +29,4 @@ export default new class autotagger {
else
return res.unsafe;
};
async getTags(id) {
if(!id)
return;
try {
const f0ck = `${cfg.main.url.full}/t/${id}.webp`;
const blob = await (await fetch(f0ck)).arrayBuffer();
const res = await fetch(`${cfg.apis.cf.url}/${cfg.apis.cf.id}/ai/run/${cfg.apis.cf.model}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${cfg.apis.cf.key}`
},
body: JSON.stringify({
image: [ ...new Uint8Array(blob) ],
prompt: "Generate eight comma separated tags without colors for this image.",
max_tokens: 128
})
});
const json = await res.json();
console.log(json.result.description);
return parseOutput(json.result.description);
} catch(err) {
return {
error: err.message || "unknown error"
};
}
};
};

View File

@ -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;

View File

@ -31,11 +31,11 @@ export default (router, tpl) => {
return res.reply({ body: "user doesn't exist or wrong password" });
const stamp = ~~(Date.now() / 1e3);
/*await db`
await db`
delete from user_sessions
where last_used <= ${(Date.now() - 6048e5)}
where last_action <= ${(Date.now() - 6048e5)}
and kmsi = 0
`;*/
`;
const session = lib.md5(lib.createID());
const blah = {
@ -44,12 +44,13 @@ export default (router, tpl) => {
browser: req.headers["user-agent"],
created_at: stamp,
last_used: stamp,
last_action: "/login",
kmsi: typeof req.post.kmsi !== 'undefined' ? 1 : 0
};
await db`
insert into "user_sessions" ${
db(blah, 'user_id', 'session', 'browser', 'created_at', 'last_used', 'kmsi')
db(blah, 'user_id', 'session', 'browser', 'created_at', 'last_used', 'last_action', 'kmsi')
}
`;

View File

@ -187,7 +187,7 @@ export default router => {
return res.json(q, tagname === newtag ? 200 : 201); // created (modified)
});
group.get(/\/admin\/tags\/suggest$/, async (req, res) => {
group.get(/\/admin\/tags\/suggest$/, lib.loggedin, async (req, res) => {
const reply = {
success: false,
suggestions: {}
@ -202,7 +202,7 @@ export default router => {
try {
const q = await db`
select tag, CAST(coalesce(count(tags_assign.tag_id), '0') AS integer) AS tagged
select tag, count(tags_assign.tag_id) as tagged
from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id
where normalized like '%' || slugify(${searchString}) || '%'
@ -211,7 +211,7 @@ export default router => {
limit 15
`;
reply.success = true;
reply.suggestions = search(q, searchString).filter(e => typeof e === 'object');
reply.suggestions = search(q, searchString);
} catch(err) {
reply.error = err.msg;
}

View File

@ -10,7 +10,7 @@ const auth = async (req, res, next) => {
};
export default (router, tpl) => {
router.get(/\/user\/(?<user>.*)/, lib.loggedin, async (req, res) => {
router.get(/\/user\/(?<user>.*)/, async (req, res) => {
const user = decodeURIComponent(req.params.user);
const query = await db`
@ -82,9 +82,6 @@ export default (router, tpl) => {
router.get(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/, async (req, res) => {
const mode = req.params.itemid ? 'item' : 'index';
if(mode === 'item' && !req.session)
return res.redirect('/login');
const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({
user: req.params.user,
tag: req.params.tag,

View File

@ -1,9 +1,8 @@
import cfg from "../../inc/config.mjs";
import lib from "../lib.mjs";
import f0cklib from "../routeinc/f0cklib.mjs";
export default (router, tpl) => {
router.get(/^\/random$/, lib.loggedin, async (req, res) => {
router.get(/^\/random$/, async (req, res) => {
let referer = req.headers.referer ?? '';
let opts = {};
@ -34,7 +33,7 @@ export default (router, tpl) => {
});
}
res.redirect(encodeURI(`${data.link.main}${data.link.path}${data.itemid}`));
res.redirect(`${data.link.main}${data.link.path}${data.itemid}`);
});
return router;
};

View File

@ -73,7 +73,7 @@ export default async bot => {
// is instagram
try {
// @flummi -> is there a variable for the actual work directory so it doesn't have to be hardcoded?
const meta = JSON.parse((await queue.exec(`yt-dlp --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
const meta = JSON.parse((await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
ext = meta.ext;
} catch(err) {
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
@ -88,7 +88,7 @@ export default async bot => {
else {
// is not instagram
try {
const meta = JSON.parse((await queue.exec(`yt-dlp --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' -I 1 --skip-download --dump-json "${link}"`)).stdout);
const meta = JSON.parse((await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
ext = meta.ext;
} catch(err) {
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
@ -98,7 +98,6 @@ export default async bot => {
}
if(!Object.values(cfg.mimes).includes(ext?.toLowerCase())) {
await e.reply("i hoab mir ind'hos'n g'schnissn schau moa noachm maim du f1ck");
return console.log('mime schmime ' + ext);
}
@ -113,7 +112,7 @@ export default async bot => {
if(link.match(regex.instagram)) {
try {
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited
source = (await queue.exec(`yt-dlp --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
} catch(err) {
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "instagram dl error");
@ -123,8 +122,7 @@ export default async bot => {
else if(link.match(regex.imgur)) {
// imghure via torsocks
try {
//await queue.exec(`torsocks wget ${link} -O ./tmp/${uuid}.${ext}`);
await queue.exec(`curl -x ${cfg.main.socks} ${link} --output ./tmp/${uuid}.${ext}`);
await queue.exec(`torsocks wget ${link} -O ./tmp/${uuid}.${ext}`);
source = `./tmp/${uuid}.${ext}`;
} catch(err) {
console.error('err:', err);
@ -135,7 +133,7 @@ export default async bot => {
}
else {
try {
source = (await queue.exec(`yt-dlp --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' -I 1 "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
} catch(err) {
console.error('err:', err);
if(e.type == 'tg')
@ -249,18 +247,35 @@ 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);
speed = !Number.isFinite(speed) ? "yes" : `${speed.toFixed(2)} Mbit/s`;
// autotagger
let tags = [];
/*if(cfg.apis?.nsfw1 && mime.startsWith('image')) {
const nsfw = await autotagger.isNSFW(filename, size);
tags.push(nsfw ? 'nsfw' : 'sfw');
if(nsfw)
await queue.tagNSFW(itemid);
else
await queue.tagSFW(itemid);
}*/
let outputmsgirc = `[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`;
let outputmsgtg = `[f0cked] size: ${lib.formatSize(size)} | speed: ${speed}`;
if(tags?.length > 0) {
const tagstr = tags.join(', ');
outputmsgirc += ` | tags: ${tagstr}`;
outputmsgtg += ` | tags: ${tagstr}`;
}
if(e.type == 'tg') {
await e.deleteMessage(msg.result.chat.id, msg.result.message_id);
/*await e.reply(outputmsgtg, {
await e.reply(outputmsgtg, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(itemid, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${itemid}` },
@ -271,42 +286,6 @@ export default async bot => {
{ text: `open f0ck #${itemid}`, url: `${cfg.main.url.full}/${itemid}` }
]]
})
});*/
let tags;
try {
tags = (await autotagger.getTags(itemid))
.map(t => ({ text: t, callback_data: 'nope' }));
await e.reply(outputmsgtg, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(itemid, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${itemid}` },
{ text: (await lib.hasTag(itemid, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${itemid}` },
],
[ ...tags.splice(0, 4) ],
[ ...tags.splice(0, 4) ],
[
{ text: `open f0ck #${itemid}`, url: `${cfg.main.url.full}/${itemid}` },
{ text: '❌ delete', callback_data: `b_delete:${itemid}` }
]]
})
});
return;
} catch(err) {
console.error(err);
}
await e.reply(outputmsgtg, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(itemid, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${itemid}` },
{ text: (await lib.hasTag(itemid, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${itemid}` },
], [
{ text: `open f0ck #${itemid}`, url: `${cfg.main.url.full}/${itemid}` },
{ text: '❌ delete', callback_data: `b_delete:${itemid}` }
]]
})
});
}
else {

View File

@ -94,8 +94,9 @@ process.on('unhandledRejection', err => {
update "user_sessions" set ${
db({
last_used: ~~(Date.now() / 1e3),
last_action: req.url.pathname,
browser: req.headers['user-agent']
}, 'last_used', 'browser')
}, 'last_used', 'last_action', 'browser')
}
where id = ${+user[0].sess_id}
`;

View File

@ -26,7 +26,7 @@
<ul>
<li>You must be 18 years or older to visit or post</li>
<li>You shall not post animal cruelty, we like our animals alive and well, living a happy life until they are ready for our Schnitzel!</li>
<li>You shall not post under <b>ANY</b> circumstances: Snuff, Beastiality, Rape, Terrorist stuff (Beheadings, First person shootings, warcrimes), Politics, News, Childporn, Childmodeling</li>
<li>You shall not post under <b>ANY</b> circumstances: Snuff, Beastiality, Rape, Terrorist stuff (Beheadings, First person shootings, warcrimes), Childporn, Childmodeling</li>
</ul>
<h5>f0cked up?</h5>
<p>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</p>
@ -38,6 +38,11 @@
<p>f0ck is developed and tested for Firefox and Chromium in their latest versions</p>
<p>If you encounter bugs please report them so we can fix them.</p>
<p>Microsoft Edgy is not actively supported, but if it werks, great! Same for anything apple related.</p>
<h5>Tinfoil f0ckers listen!</h5>
<p>f0ck onions and moons, but fockulite!</p>
<p>http://fockmoonsb24iczs7odozzy5uktlzbcgp337nabrgffzxv5ihabgpvyd.onion</p>
<p>http://fockulite74atso2xsxxw6q2gzqrgck572tiwvkyf5vdxictjn2vmlyd.onion</p>
<p>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.</p>
<h5>f0ck Privacy?</h5>
<p>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"</p>
<p>Logs: We do not log users accessing our website.</p>

View File

@ -10,6 +10,7 @@
<td>browser</td>
<td>created_at</td>
<td>last_used</td>
<td>last_action</td>
</tr>
</thead>
<tbody>
@ -22,6 +23,7 @@
<td>{{ session.browser }}</td>
<td>{{ new Date(session.created_at * 1e3).toLocaleString("de-DE") }}</td>
<td>{{ new Date(session.last_used * 1e3).toLocaleString("de-DE") }}</td>
<td>{{ session.last_action }}</td>
</tr>
@endeach
</tbody>

View File

@ -9,7 +9,7 @@
<body type="login">
<form class="login-form" method="post" action="/login">
<p><a href="/"><img src="/s/img/f0ck_small.png" /></a></p>
<input type="text" name="username" placeholder="username" autocomplete="off" autofocus required />
<input type="text" name="username" placeholder="username" autocomplete="off" required />
<input type="password" name="password" placeholder="password" autocomplete="off" required />
<p><input type="checkbox" id="kmsi" name="kmsi" /> <label for="kmsi">stay signed in</label></p>
<button type="submit">Login</button>

View File

@ -1,12 +1,21 @@
@include(snippets/header)
<div id="main">
<div class="topf0ckers">
<h3>Top f0ckers of all time <br>- Ranking -</h3>
<h3>f0ck Rankings?</h3>
</div>
<div class="theforceofthree">
<div class="triin">
</div>
<div class="ranking">
<div class="by-user">
<h3>Biggest taggers</h3>
<table class="table">
<tr>
<th>Rank</th>
<th>Avatar</th>
<th>Username</th>
<th>Tagged</th>
</tr>
<tbody>
@for(let i = 0; i < list.length; i++)
<tr>
@ -47,5 +56,8 @@
</table>
</div>
</div>
<div class="triout">
</div>
</div>
</div>
@include(snippets/footer)

View File

@ -2,10 +2,6 @@
<script async src="/s/js/theme.js?v=@mtime(/public/s/js/theme.js)"></script>
<script src="/s/js/v0ck.js?v=@mtime(/public/s/js/v0ck.js)"></script>
<script src="/s/js/f0ck.js?v=@mtime(/public/s/js/f0ck.js)"></script>
@if(session && session.admin)
<script src="/s/js/admin.js?v=@mtime(/public/s/js/admin.js)"></script>
@elseif(session && !session.admin)
<script src="/s/js/user.js?v=@mtime(/public/s/js/user.js)"></script>
@endif
@if(session)<script src="/s/js/admin.js?v=@mtime(/public/s/js/admin.js)"></script>@endif
</body>
</html>

View File

@ -5,7 +5,7 @@
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown">
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar"><span class="navUserName">@if(session.admin)&#11088;&nbsp;@endif{{ session.user }}</span>
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar"><span>@if(session.admin)&#11088;&nbsp;@endif{{ session.user }}</span>
</a>
<ul class="dropdown-menu">
<li><a href="/user/{{ session.user.toLowerCase() }}">my profile</a></li>