import { Brackets } from "typeorm"; import { Notes } from "../../../../models/index.js"; import define from "../../define.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js"; import { generateVisibilityQuery } from "../../common/generate-visibility-query.js"; function hasTag(note, tag) { return note.tags?.some((x)=>x.toLowerCase() === tag.toLowerCase()) ?? false; } function mediaServiceMatches(note, kind) { const serviceTag = kind === "video" ? "VideoService" : kind === "audio" ? "AudioService" : "ImageService"; return [ "public", "home", "followers" ].includes(note.visibility) && hasTag(note, serviceTag) && hasValidMediaFiles(note.files ?? [], kind); } function hasValidMediaFiles(files, kind) { const mediaFiles = files.filter((file)=>file.type.startsWith(`${kind}/`)); if (kind === "image") { return mediaFiles.length >= 1 && files.every((file)=>file.type.startsWith("image/")); } return mediaFiles.length >= 1 && files.every((file)=>file.type.startsWith(`${kind}/`) || file.type.startsWith("image/")); } export const meta = { tags: [ "notes" ], requireCredential: false, requireCredentialPrivateMode: true, res: { type: "array", optional: false, nullable: false, items: { type: "object", optional: false, nullable: false, ref: "Note" } } }; export const paramDef = { type: "object", properties: { kind: { type: "string", enum: [ "video", "audio", "image" ] }, query: { type: "string", default: "" }, limit: { type: "integer", minimum: 1, maximum: 50, default: 20 }, offset: { type: "integer", minimum: 0, default: 0 } }, required: [ "kind" ] }; export default define(meta, paramDef, async (ps, user)=>{ const serviceTag = ps.kind === "video" ? "videoservice" : ps.kind === "audio" ? "audioservice" : "imageservice"; const queryText = ps.query.trim(); const query = Notes.createQueryBuilder("note").innerJoinAndSelect("note.user", "user").leftJoinAndSelect("note.reply", "reply").leftJoinAndSelect("note.renote", "renote").leftJoinAndSelect("reply.user", "replyUser").leftJoinAndSelect("renote.user", "renoteUser").where(`'{"${serviceTag}"}' <@ note.tags`).andWhere("note.fileIds != '{}'"); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); if (queryText.length > 0) { query.andWhere(new Brackets((qb)=>{ qb.where("note.text ILIKE :query", { query: `%${queryText}%` }).orWhere("user.usernameLower ILIKE :query", { query: `%${queryText.toLowerCase()}%` }); })); } const notes = await query.orderBy("note.createdAt", "DESC").skip(ps.offset).take(ps.limit * 3).getMany(); const packed = await Notes.packMany(notes, user); return packed.filter((note)=>mediaServiceMatches(note, ps.kind)).slice(0, ps.limit); });