Merge branch 'f0bm' into eins-f0bm
This commit is contained in:
@@ -1,15 +0,0 @@
|
|||||||
import db from "../src/inc/sql.mjs";
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const item = (await db`select * from items order by id desc limit 1`)?.[0];
|
|
||||||
console.log("Last Item:", item);
|
|
||||||
if (item) {
|
|
||||||
const tags = await db`select * from tags_assign where item_id = ${item.id}`;
|
|
||||||
console.log("Tags:", tags);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
process.exit(0);
|
|
||||||
})();
|
|
||||||
@@ -6,14 +6,13 @@ import { promises as fs } from "fs";
|
|||||||
network: "console",
|
network: "console",
|
||||||
message: _args.join(" "),
|
message: _args.join(" "),
|
||||||
args: _args.slice(1),
|
args: _args.slice(1),
|
||||||
channel: "#w0bm",
|
channel: "console",
|
||||||
user: {
|
user: {
|
||||||
prefix: "console!console@console",
|
prefix: "console!console@console",
|
||||||
nick: "console",
|
nick: "console",
|
||||||
username: "console",
|
username: "console",
|
||||||
account: "console"
|
account: "console"
|
||||||
},
|
},
|
||||||
raw: {},
|
|
||||||
reply: (...args) => console.log(args),
|
reply: (...args) => console.log(args),
|
||||||
replyAction: (...args) => console.log(args),
|
replyAction: (...args) => console.log(args),
|
||||||
replyNotice: (...args) => console.log(args)
|
replyNotice: (...args) => console.log(args)
|
||||||
@@ -27,12 +26,9 @@ import { promises as fs } from "fs";
|
|||||||
try {
|
try {
|
||||||
if(trigger.length === 0)
|
if(trigger.length === 0)
|
||||||
return console.error("no matches");
|
return console.error("no matches");
|
||||||
for (const t of trigger) {
|
console.log(`triggered > ${trigger[0].name} (${_e.message})`);
|
||||||
console.log(`triggered > ${t.name} (${_e.message})`);
|
await trigger[0].f(_e);
|
||||||
await t.f(_e);
|
|
||||||
}
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
process.exit(0);
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -3058,4 +3058,69 @@ input#s_avatar {
|
|||||||
50% {
|
50% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
/* Modern Tags Layout */
|
||||||
|
.tags-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--badge-bg, #171717);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-decoration: none !important;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
border: 1px solid var(--nav-border-color, rgba(255,255,255,0.1));
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0,0,0,0.4);
|
||||||
|
background: var(--dropdown-bg, #232323);
|
||||||
|
border-color: var(--accent, #9f0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card-image img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.5s;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card:hover .tag-card-image img {
|
||||||
|
transform: scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-card-content {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-name {
|
||||||
|
color: var(--white, #fff);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-family: var(--font, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-count {
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
41
src/inc/routes/tag_image.mjs
Normal file
41
src/inc/routes/tag_image.mjs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export default (router, tpl) => {
|
||||||
|
router.get(/^\/tag_image\/(?<tag>.+)$/, async (req, res) => {
|
||||||
|
const tag = req.params.tag;
|
||||||
|
|
||||||
|
// Create a deterministic hash from the tag
|
||||||
|
const hash = crypto.createHash('md5').update(tag).digest('hex');
|
||||||
|
|
||||||
|
// Generate colors from hash
|
||||||
|
const c1 = '#' + hash.substring(0, 6);
|
||||||
|
const c2 = '#' + hash.substring(6, 12);
|
||||||
|
const c3 = '#' + hash.substring(12, 18);
|
||||||
|
|
||||||
|
// Generate some deterministic numbers for shapes
|
||||||
|
const n1 = parseInt(hash.substring(18, 20), 16);
|
||||||
|
const n2 = parseInt(hash.substring(20, 22), 16);
|
||||||
|
|
||||||
|
const svg = `
|
||||||
|
<svg width="300" height="150" viewBox="0 0 300 150" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:${c1};stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:${c2};stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="300" height="150" fill="url(#grad)" />
|
||||||
|
<circle cx="${n1}%" cy="${n2}%" r="${(n1 + n2) / 4}" fill="${c3}" fill-opacity="0.3" />
|
||||||
|
<circle cx="${100 - n1}%" cy="${100 - n2}%" r="${(n1 + n2) / 3}" fill="${c3}" fill-opacity="0.2" />
|
||||||
|
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="sans-serif" font-size="24" fill="#fff" fill-opacity="0.9" font-weight="bold">${tag}</text>
|
||||||
|
</svg>
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'image/svg+xml',
|
||||||
|
'Cache-Control': 'public, max-age=86400'
|
||||||
|
});
|
||||||
|
res.end(svg);
|
||||||
|
});
|
||||||
|
return router;
|
||||||
|
};
|
||||||
@@ -23,7 +23,7 @@ export default async bot => {
|
|||||||
name: "parser",
|
name: "parser",
|
||||||
call: regex.all,
|
call: regex.all,
|
||||||
active: true,
|
active: true,
|
||||||
f: async e => {
|
f: e => {
|
||||||
const links = e.message.match(regex.all)?.filter(link => !link.includes(cfg.main.url.domain)) || [];
|
const links = e.message.match(regex.all)?.filter(link => !link.includes(cfg.main.url.domain)) || [];
|
||||||
let repost;
|
let repost;
|
||||||
if(e.media)
|
if(e.media)
|
||||||
@@ -51,7 +51,7 @@ export default async bot => {
|
|||||||
|
|
||||||
console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`);
|
console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`);
|
||||||
|
|
||||||
for (const link of links) {
|
links.forEach(async link => {
|
||||||
//if(regex.imgur.test(link))
|
//if(regex.imgur.test(link))
|
||||||
// return await e.reply(`fuck imgur... seriously`);
|
// return await e.reply(`fuck imgur... seriously`);
|
||||||
|
|
||||||
@@ -73,12 +73,11 @@ export default async bot => {
|
|||||||
|
|
||||||
// read metadata
|
// read metadata
|
||||||
let ext;
|
let ext;
|
||||||
const proxyArgs = cfg.main.socks ? `--proxy ${cfg.main.socks}` : '';
|
|
||||||
if(link.match(regex.instagram)) {
|
if(link.match(regex.instagram)) {
|
||||||
// is instagram
|
// is instagram
|
||||||
try {
|
try {
|
||||||
// @flummi -> is there a variable for the actual work directory so it doesn't have to be hardcoded?
|
// @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 ${proxyArgs} -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 --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
||||||
ext = meta.ext;
|
ext = meta.ext;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||||
@@ -93,10 +92,9 @@ export default async bot => {
|
|||||||
else if(link.match(regex.yt)) {
|
else if(link.match(regex.yt)) {
|
||||||
//yt - fuck anti bot protection
|
//yt - fuck anti bot protection
|
||||||
try {
|
try {
|
||||||
const meta = JSON.parse((await queue.exec(`yt-dlp ${proxyArgs} -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 --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' -I 1 --skip-download --dump-json "${link}"`)).stdout);
|
||||||
ext = meta.ext;
|
ext = meta.ext;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error("YT-DLP Error:", err);
|
|
||||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||||
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
|
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
|
||||||
ext = cfg.mimes[tmphead];
|
ext = cfg.mimes[tmphead];
|
||||||
@@ -105,7 +103,7 @@ export default async bot => {
|
|||||||
else if(link.match(regex.fourchan)) {
|
else if(link.match(regex.fourchan)) {
|
||||||
//4chan - fuck cloudflare :)
|
//4chan - fuck cloudflare :)
|
||||||
try {
|
try {
|
||||||
const meta = JSON.parse((await queue.exec(`yt-dlp ${proxyArgs} -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 --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
|
||||||
ext = meta.ext;
|
ext = meta.ext;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||||
@@ -120,10 +118,9 @@ export default async bot => {
|
|||||||
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);
|
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;
|
ext = meta.ext;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error('err:', err);
|
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
|
||||||
if (e.type == 'tg')
|
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
|
||||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err);
|
ext = cfg.mimes[tmphead];
|
||||||
return await e.reply('something went wrong lol / check maxfilesize?');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +139,7 @@ export default async bot => {
|
|||||||
if(link.match(regex.instagram)) {
|
if(link.match(regex.instagram)) {
|
||||||
try {
|
try {
|
||||||
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited or optionally use a socks proxy from a network that is not being detected as a public network
|
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited or optionally use a socks proxy from a network that is not being detected as a public network
|
||||||
source = (await queue.exec(`yt-dlp ${proxyArgs} -f 'bv*[height<=1080]+ba/b[height<=1080] / 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 --proxy ${cfg.main.socks} -f 'bv*[height<=1080]+ba/b[height<=1080] / 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) {
|
} catch(err) {
|
||||||
if(e.type == 'tg')
|
if(e.type == 'tg')
|
||||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "instagram dl error");
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "instagram dl error");
|
||||||
@@ -164,7 +161,7 @@ export default async bot => {
|
|||||||
else if(link.match(regex.yt)) {
|
else if(link.match(regex.yt)) {
|
||||||
try {
|
try {
|
||||||
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited or optionally use a socks proxy from a network that is not being detected as a public network
|
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited or optionally use a socks proxy from a network that is not being detected as a public network
|
||||||
source = (await queue.exec(`yt-dlp ${proxyArgs} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" -I 1 --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 --proxy ${cfg.main.socks} -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" -I 1 --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) {
|
} catch(err) {
|
||||||
if(e.type == 'tg')
|
if(e.type == 'tg')
|
||||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "yt dl error");
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "yt dl error");
|
||||||
@@ -174,7 +171,7 @@ export default async bot => {
|
|||||||
else if(link.match(regex.fourchan)) {
|
else if(link.match(regex.fourchan)) {
|
||||||
// 4chan via proxy - fuck cloudflare
|
// 4chan via proxy - fuck cloudflare
|
||||||
try {
|
try {
|
||||||
source = (await queue.exec(`yt-dlp ${proxyArgs} -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 --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();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if(e.type == 'tg')
|
if(e.type == 'tg')
|
||||||
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "yt dl error");
|
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "yt dl error");
|
||||||
@@ -276,7 +273,8 @@ export default async bot => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await db`
|
await db`
|
||||||
insert into items ${db({
|
insert into items ${
|
||||||
|
db({
|
||||||
src: e.media ? "" : link,
|
src: e.media ? "" : link,
|
||||||
dest: filename,
|
dest: filename,
|
||||||
mime: mime,
|
mime: mime,
|
||||||
@@ -293,16 +291,6 @@ export default async bot => {
|
|||||||
|
|
||||||
const itemid = await queue.getItemID(filename);
|
const itemid = await queue.getItemID(filename);
|
||||||
|
|
||||||
// auto-tag sfw
|
|
||||||
await db`
|
|
||||||
insert into tags_assign ${db({
|
|
||||||
item_id: itemid,
|
|
||||||
tag_id: 1, // sfw
|
|
||||||
user_id: 2 // f0ck
|
|
||||||
}, 'item_id', 'tag_id', 'user_id')
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// generate thumbnail
|
// generate thumbnail
|
||||||
try {
|
try {
|
||||||
await queue.genThumbnail(filename, mime, itemid, link);
|
await queue.genThumbnail(filename, mime, itemid, link);
|
||||||
@@ -351,7 +339,7 @@ export default async bot => {
|
|||||||
else {
|
else {
|
||||||
await e.reply(outputmsgirc);
|
await e.reply(outputmsgirc);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<!-- logged in -->
|
<!-- logged in -->
|
||||||
<nav class="navbar navbar-expand-lg">
|
<nav class="navbar navbar-expand-lg">
|
||||||
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">w0bm.com</span></a>
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">w0bm.com</span></a>
|
||||||
|
|
||||||
<div class="navigation-links-guest">
|
<div class="navigation-links-guest">
|
||||||
<ol>
|
<ol>
|
||||||
<a href="/tags">tags</a>
|
<a href="/tags">tags</a>
|
||||||
@@ -13,7 +12,7 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<!-- show pagination only for tags and main page -->
|
<!-- show pagination only for tags and main page -->
|
||||||
@if(!/^\/\d$/.test(url.pathname))
|
@if(!/^\/\d+$/.test(url.pathname))
|
||||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
||||||
<div class="pagination-container-fluid">
|
<div class="pagination-container-fluid">
|
||||||
<div class="pagination-wrapper">
|
<div class="pagination-wrapper">
|
||||||
@@ -27,7 +26,6 @@
|
|||||||
<!-- not logged in -->
|
<!-- not logged in -->
|
||||||
<nav class="navbar navbar-expand-lg">
|
<nav class="navbar navbar-expand-lg">
|
||||||
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">w0bm.com</span></a>
|
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">w0bm.com</span></a>
|
||||||
|
|
||||||
<div class="navigation-links-guest">
|
<div class="navigation-links-guest">
|
||||||
<ol>
|
<ol>
|
||||||
<a href="/tags">tags</a>
|
<a href="/tags">tags</a>
|
||||||
@@ -38,7 +36,7 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<!-- show pagination only for tags and main page -->
|
<!-- show pagination only for tags and main page -->
|
||||||
@if(!/^\/\d$/.test(url.pathname))
|
@if(!/^\/\d+$/.test(url.pathname))
|
||||||
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
|
||||||
<div class="pagination-container-fluid">
|
<div class="pagination-container-fluid">
|
||||||
<div class="pagination-wrapper">
|
<div class="pagination-wrapper">
|
||||||
|
|||||||
@@ -2,24 +2,30 @@
|
|||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 style="text-align: center;">☯</h3>
|
<h3 style="text-align: center;">☯</h3>
|
||||||
<div class="tags">
|
<div class="tags-grid">
|
||||||
@if(session)
|
@if(session)
|
||||||
@each(toptags_regged as toptag)
|
@each(toptags_regged as toptag)
|
||||||
<div class="tag badge badge-light mr-2">
|
<a href="/tag/{!! toptag.tag !!}" class="tag-card">
|
||||||
<div class="tagbox-body">
|
<div class="tag-card-image">
|
||||||
<span class="toptag_id">{!! toptag.tag !!}</span>
|
<img src="/tag_image/{!! toptag.tag !!}" loading="lazy" alt="{!! toptag.tag !!}">
|
||||||
<span class="toptag_tag"><a href="/tag/{!! toptag.tag !!}">{{ toptag.total_items }}</a></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tag-card-content">
|
||||||
|
<span class="tag-name">#{!! toptag.tag !!}</span>
|
||||||
|
<span class="tag-count">{{ toptag.total_items }} posts</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
@endeach
|
@endeach
|
||||||
@else
|
@else
|
||||||
@each(toptags as toptag)
|
@each(toptags as toptag)
|
||||||
<div class="tag badge badge-light mr-2">
|
<a href="/tag/{!! toptag.tag !!}" class="tag-card">
|
||||||
<div class="tagbox-body">
|
<div class="tag-card-image">
|
||||||
<span class="toptag_id">{!! toptag.tag !!}</span>
|
<img src="/tag_image/{!! toptag.tag !!}" loading="lazy" alt="{!! toptag.tag !!}">
|
||||||
<span class="toptag_tag"><a href="/tag/{!! toptag.tag !!}">{{ toptag.total_items }}</a></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tag-card-content">
|
||||||
|
<span class="tag-name">#{!! toptag.tag !!}</span>
|
||||||
|
<span class="tag-count">{{ toptag.total_items }} posts</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
@endeach
|
@endeach
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user