Add file-type and host filters with a 4chan default
This commit is contained in:
+47
-1
@@ -195,11 +195,15 @@
|
||||
: "";
|
||||
const origin = state.filters.origin ? `origin=${state.filters.origin}&` : "";
|
||||
const cached = state.filters.cachedOnly ? "cachedOnly=1&" : "";
|
||||
const exts = state.filters.exts.length ? `ext=${state.filters.exts.join(",")}&` : "";
|
||||
const excludeHosts = state.filters.excludeHosts.length
|
||||
? `excludeHosts=${state.filters.excludeHosts.join(",")}&`
|
||||
: "";
|
||||
const aspect = state.filters.aspect ? `aspect=${state.filters.aspect}&` : "";
|
||||
const resolution = state.filters.resolution ? `resolution=${state.filters.resolution}&` : "";
|
||||
const after = state.filters.after ? `after=${state.filters.after}T00:00:00.000Z&` : "";
|
||||
const before = state.filters.before ? `before=${state.filters.before}T23:59:59.999Z&` : "";
|
||||
return `${kind}${source}${channel}${authors}${origin}${cached}${aspect}${resolution}${after}${before}`;
|
||||
return `${kind}${source}${channel}${authors}${origin}${cached}${exts}${excludeHosts}${aspect}${resolution}${after}${before}`;
|
||||
}
|
||||
|
||||
function kindTotals(field) {
|
||||
@@ -629,6 +633,8 @@
|
||||
authorIds: [],
|
||||
origin: "",
|
||||
cachedOnly: false,
|
||||
exts: [],
|
||||
excludeHosts: ["4chan.org", "i.4cdn.org"],
|
||||
aspect: "",
|
||||
resolution: "",
|
||||
after: "",
|
||||
@@ -650,6 +656,8 @@
|
||||
authorIds: [...state.draft.authorIds],
|
||||
origin: state.draft.origin,
|
||||
cachedOnly: state.draft.cachedOnly,
|
||||
exts: [...state.draft.exts],
|
||||
excludeHosts: [...state.draft.excludeHosts],
|
||||
aspect: state.draft.aspect,
|
||||
resolution: state.draft.resolution,
|
||||
after: state.draft.after,
|
||||
@@ -846,6 +854,34 @@
|
||||
})
|
||||
);
|
||||
|
||||
const typeRow = (label, extensions) => {
|
||||
const row = el("div", "vk-media-toolbar");
|
||||
row.append(el("span", "vk-media-status", label));
|
||||
for (const extension of extensions) {
|
||||
const chip = el(
|
||||
"button",
|
||||
`vk-chip ${draft.exts.includes(extension) ? "vk-chip-on" : "vk-chip-off"}`,
|
||||
extension
|
||||
);
|
||||
chip.type = "button";
|
||||
chip.addEventListener("click", () => {
|
||||
draft.exts = draft.exts.includes(extension)
|
||||
? draft.exts.filter(value => value !== extension)
|
||||
: [...draft.exts, extension];
|
||||
renderFilterDrawer();
|
||||
});
|
||||
row.append(chip);
|
||||
}
|
||||
return row;
|
||||
};
|
||||
|
||||
filters.append(
|
||||
el("div", "vk-media-status", "File types"),
|
||||
typeRow("Video", ["mp4", "webm", "mov", "mkv"]),
|
||||
typeRow("Image", ["png", "jpg", "gif", "webp", "avif"]),
|
||||
typeRow("Audio", ["mp3", "ogg", "wav", "m4a", "flac"])
|
||||
);
|
||||
|
||||
const cachedRow = el("label", "vk-media-check");
|
||||
const cachedBox = document.createElement("input");
|
||||
cachedBox.type = "checkbox";
|
||||
@@ -855,6 +891,16 @@
|
||||
});
|
||||
cachedRow.append(cachedBox, document.createTextNode(" Show only cached previews"));
|
||||
filters.append(cachedRow);
|
||||
|
||||
const deadRow = el("label", "vk-media-check");
|
||||
const deadBox = document.createElement("input");
|
||||
deadBox.type = "checkbox";
|
||||
deadBox.checked = draft.excludeHosts.includes("4chan.org");
|
||||
deadBox.addEventListener("change", () => {
|
||||
draft.excludeHosts = deadBox.checked ? ["4chan.org", "i.4cdn.org"] : [];
|
||||
});
|
||||
deadRow.append(deadBox, document.createTextNode(" Hide 4chan links (usually dead)"));
|
||||
filters.append(deadRow);
|
||||
filterDrawer.append(filters);
|
||||
|
||||
// Zoom applies live; it is not part of the filter draft.
|
||||
|
||||
@@ -883,6 +883,8 @@ function buildServer(): McpServer {
|
||||
authorIds: z.array(z.string()).optional().describe("Restrict to these posters"),
|
||||
origin: z.enum(["dm", "guild"]).optional().describe("Server channels or DMs"),
|
||||
cachedOnly: z.boolean().optional().describe("Only items with a cached preview"),
|
||||
exts: z.array(z.string()).optional().describe("Only these file extensions (mp4, webm, png, ...)"),
|
||||
excludeHosts: z.array(z.string()).optional().describe("Hide URLs from these hosts"),
|
||||
aspect: z
|
||||
.enum(["vertical", "square", "wide43", "wide169", "ultrawide", "other"])
|
||||
.optional()
|
||||
@@ -905,6 +907,8 @@ function buildServer(): McpServer {
|
||||
authorIds,
|
||||
origin,
|
||||
cachedOnly,
|
||||
exts,
|
||||
excludeHosts,
|
||||
aspect,
|
||||
resolution,
|
||||
after,
|
||||
@@ -921,6 +925,8 @@ function buildServer(): McpServer {
|
||||
authorIds,
|
||||
origin,
|
||||
cachedOnly,
|
||||
exts,
|
||||
excludeHosts,
|
||||
aspect,
|
||||
resolution,
|
||||
after,
|
||||
|
||||
@@ -638,6 +638,8 @@ export class MediaService {
|
||||
authorIds?: string[];
|
||||
origin?: "dm" | "guild";
|
||||
cachedOnly?: boolean;
|
||||
exts?: string[];
|
||||
excludeHosts?: string[];
|
||||
aspect?: string;
|
||||
resolution?: string;
|
||||
after?: string;
|
||||
@@ -757,6 +759,8 @@ export class MediaService {
|
||||
authorIds?: string[];
|
||||
origin?: "dm" | "guild";
|
||||
cachedOnly?: boolean;
|
||||
exts?: string[];
|
||||
excludeHosts?: string[];
|
||||
aspect?: string;
|
||||
resolution?: string;
|
||||
after?: string;
|
||||
@@ -782,6 +786,8 @@ export class MediaService {
|
||||
authorId: options.authorId,
|
||||
authorIds: options.authorIds,
|
||||
cachedOnly: options.cachedOnly,
|
||||
exts: options.exts,
|
||||
excludeHosts: options.excludeHosts,
|
||||
aspect: options.aspect,
|
||||
resolution: options.resolution,
|
||||
after: options.after,
|
||||
@@ -833,6 +839,8 @@ export class MediaService {
|
||||
authorIds?: string[];
|
||||
origin?: "dm" | "guild";
|
||||
cachedOnly?: boolean;
|
||||
exts?: string[];
|
||||
excludeHosts?: string[];
|
||||
aspect?: string;
|
||||
resolution?: string;
|
||||
after?: string;
|
||||
|
||||
@@ -893,6 +893,18 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
|
||||
? (String(req.query.origin) as "dm" | "guild")
|
||||
: undefined;
|
||||
const cachedOnly = req.query.cachedOnly === "1" || req.query.cachedOnly === "true";
|
||||
const exts = req.query.ext
|
||||
? String(req.query.ext)
|
||||
.split(",")
|
||||
.map(value => value.trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
: undefined;
|
||||
const excludeHosts = req.query.excludeHosts
|
||||
? String(req.query.excludeHosts)
|
||||
.split(",")
|
||||
.map(value => value.trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
: undefined;
|
||||
const aspect = req.query.aspect ? String(req.query.aspect) : undefined;
|
||||
const resolution = req.query.resolution ? String(req.query.resolution) : undefined;
|
||||
const after = req.query.after ? String(req.query.after) : undefined;
|
||||
@@ -909,6 +921,8 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
|
||||
authorIds,
|
||||
origin,
|
||||
cachedOnly,
|
||||
exts,
|
||||
excludeHosts,
|
||||
aspect,
|
||||
resolution,
|
||||
after,
|
||||
@@ -944,6 +958,18 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
|
||||
req.query.origin === "dm" || req.query.origin === "guild"
|
||||
? (String(req.query.origin) as "dm" | "guild")
|
||||
: undefined;
|
||||
const exts = req.query.ext
|
||||
? String(req.query.ext)
|
||||
.split(",")
|
||||
.map(value => value.trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
: undefined;
|
||||
const excludeHosts = req.query.excludeHosts
|
||||
? String(req.query.excludeHosts)
|
||||
.split(",")
|
||||
.map(value => value.trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
: undefined;
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
@@ -954,6 +980,8 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
|
||||
authorIds,
|
||||
origin,
|
||||
cachedOnly: req.query.cachedOnly === "1" || req.query.cachedOnly === "true",
|
||||
exts,
|
||||
excludeHosts,
|
||||
aspect: req.query.aspect ? String(req.query.aspect) : undefined,
|
||||
resolution: req.query.resolution ? String(req.query.resolution) : undefined,
|
||||
after: req.query.after ? String(req.query.after) : undefined,
|
||||
|
||||
Reference in New Issue
Block a user