mirror of
https://github.com/sim1222/misskey.git
synced 2025-08-04 15:53:51 +09:00
feat: auto nsfw detection (#8840)
* feat: auto nsfw detection * ✌️ * Update ja-JP.yml * Update ja-JP.yml * ポルノ判定のしきい値を高めに * エラーハンドリングちゃんとした * Update ja-JP.yml * 感度設定を強化 * refactor * feat: add video support for auto nsfw detection * rename: image -> media * .js * fix: add missing error handling * fix: use valid pathname instead of using filename due to invalid usage * perf(nsfw-detection): decode frames * disable detection of video for some reasons * perf(nsfw-detection): streamify detection process for video * disable disallowUploadWhenPredictedAsPorn option * fix(nsfw-detection): improve reliability * fix(nsfw-detection): use Math.ceil instead of Math.round * perf(nsfw-detection): delete tmp frames after used * fix(nsfw-detection): FSWatcher does not emit ready event * perf(nsfw-detection): skip black frames * refactor: strip exists check * Update package.json * めっちゃ変えた * lint * Update COPYING * オプションで動画解析できるように * Update yarn.lock * Update CHANGELOG.md Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
This commit is contained in:
@ -195,6 +195,22 @@ export const meta = {
|
||||
type: 'string',
|
||||
optional: true, nullable: true,
|
||||
},
|
||||
sensitiveMediaDetection: {
|
||||
type: 'string',
|
||||
optional: true, nullable: false,
|
||||
},
|
||||
sensitiveMediaDetectionSensitivity: {
|
||||
type: 'string',
|
||||
optional: true, nullable: false,
|
||||
},
|
||||
setSensitiveFlagAutomatically: {
|
||||
type: 'boolean',
|
||||
optional: true, nullable: false,
|
||||
},
|
||||
enableSensitiveMediaDetectionForVideos: {
|
||||
type: 'boolean',
|
||||
optional: true, nullable: false,
|
||||
},
|
||||
proxyAccountId: {
|
||||
type: 'string',
|
||||
optional: true, nullable: true,
|
||||
@ -370,6 +386,10 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
blockedHosts: instance.blockedHosts,
|
||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||
recaptchaSecretKey: instance.recaptchaSecretKey,
|
||||
sensitiveMediaDetection: instance.sensitiveMediaDetection,
|
||||
sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity,
|
||||
setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically,
|
||||
enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos,
|
||||
proxyAccountId: instance.proxyAccountId,
|
||||
twitterConsumerKey: instance.twitterConsumerKey,
|
||||
twitterConsumerSecret: instance.twitterConsumerSecret,
|
||||
|
@ -58,6 +58,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
autoAcceptFollowed: profile.autoAcceptFollowed,
|
||||
noCrawle: profile.noCrawle,
|
||||
alwaysMarkNsfw: profile.alwaysMarkNsfw,
|
||||
autoSensitive: profile.autoSensitive,
|
||||
carefulBot: profile.carefulBot,
|
||||
injectFeaturedNote: profile.injectFeaturedNote,
|
||||
receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
|
||||
|
@ -48,6 +48,10 @@ export const paramDef = {
|
||||
enableRecaptcha: { type: 'boolean' },
|
||||
recaptchaSiteKey: { type: 'string', nullable: true },
|
||||
recaptchaSecretKey: { type: 'string', nullable: true },
|
||||
sensitiveMediaDetection: { type: 'string', enum: ['none', 'all', 'local', 'remote'] },
|
||||
sensitiveMediaDetectionSensitivity: { type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'] },
|
||||
setSensitiveFlagAutomatically: { type: 'boolean' },
|
||||
enableSensitiveMediaDetectionForVideos: { type: 'boolean' },
|
||||
proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
maintainerName: { type: 'string', nullable: true },
|
||||
maintainerEmail: { type: 'string', nullable: true },
|
||||
@ -213,6 +217,22 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
set.recaptchaSecretKey = ps.recaptchaSecretKey;
|
||||
}
|
||||
|
||||
if (ps.sensitiveMediaDetection !== undefined) {
|
||||
set.sensitiveMediaDetection = ps.sensitiveMediaDetection;
|
||||
}
|
||||
|
||||
if (ps.sensitiveMediaDetectionSensitivity !== undefined) {
|
||||
set.sensitiveMediaDetectionSensitivity = ps.sensitiveMediaDetectionSensitivity;
|
||||
}
|
||||
|
||||
if (ps.setSensitiveFlagAutomatically !== undefined) {
|
||||
set.setSensitiveFlagAutomatically = ps.setSensitiveFlagAutomatically;
|
||||
}
|
||||
|
||||
if (ps.enableSensitiveMediaDetectionForVideos !== undefined) {
|
||||
set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos;
|
||||
}
|
||||
|
||||
if (ps.proxyAccountId !== undefined) {
|
||||
set.proxyAccountId = ps.proxyAccountId;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import ms from 'ms';
|
||||
import { addFile } from '@/services/drive/add-file.js';
|
||||
import { DriveFiles } from '@/models/index.js';
|
||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||
import define from '../../../define.js';
|
||||
import { apiLogger } from '../../../logger.js';
|
||||
@ -35,6 +36,18 @@ export const meta = {
|
||||
code: 'INVALID_FILE_NAME',
|
||||
id: 'f449b209-0c60-4e51-84d5-29486263bfd4',
|
||||
},
|
||||
|
||||
inappropriate: {
|
||||
message: 'Cannot upload the file because it has been determined that it possibly contains inappropriate content.',
|
||||
code: 'INAPPROPRIATE',
|
||||
id: 'bec5bd69-fba3-43c9-b4fb-2894b66ad5d2',
|
||||
},
|
||||
|
||||
noFreeSpace: {
|
||||
message: 'Cannot upload the file because you have no free space of drive.',
|
||||
code: 'NO_FREE_SPACE',
|
||||
id: 'd08dbc37-a6a9-463a-8c47-96c32ab5f064',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
@ -87,6 +100,10 @@ export default define(meta, paramDef, async (ps, user, _, file, cleanup, ip, hea
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
apiLogger.error(e);
|
||||
}
|
||||
if (e instanceof IdentifiableError) {
|
||||
if (e.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate);
|
||||
if (e.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace);
|
||||
}
|
||||
throw new ApiError();
|
||||
} finally {
|
||||
cleanup!();
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { publishDriveStream } from '@/services/stream.js';
|
||||
import define from '../../../define.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
import { DriveFiles, DriveFolders, Users } from '@/models/index.js';
|
||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
|
||||
import define from '../../../define.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['drive'],
|
||||
|
@ -3,17 +3,17 @@ import * as mfm from 'mfm-js';
|
||||
import { publishMainStream, publishUserEvent } from '@/services/stream.js';
|
||||
import acceptAllFollowRequests from '@/services/following/requests/accept-all.js';
|
||||
import { publishToFollowers } from '@/services/i/update.js';
|
||||
import define from '../../define.js';
|
||||
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
||||
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
||||
import { updateUsertags } from '@/services/update-hashtag.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index.js';
|
||||
import { User } from '@/models/entities/user.js';
|
||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||
import { notificationTypes } from '@/types.js';
|
||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||
import { langmap } from '@/misc/langmap.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import define from '../../define.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['account'],
|
||||
@ -57,7 +57,7 @@ export const meta = {
|
||||
message: 'Invalid Regular Expression.',
|
||||
code: 'INVALID_REGEXP',
|
||||
id: '0d786918-10df-41cd-8f33-8dec7d9a89a5',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
@ -77,7 +77,8 @@ export const paramDef = {
|
||||
lang: { type: 'string', enum: [null, ...Object.keys(langmap)], nullable: true },
|
||||
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
fields: { type: 'array',
|
||||
fields: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
maxItems: 16,
|
||||
items: {
|
||||
@ -102,6 +103,7 @@ export const paramDef = {
|
||||
injectFeaturedNote: { type: 'boolean' },
|
||||
receiveAnnouncementEmail: { type: 'boolean' },
|
||||
alwaysMarkNsfw: { type: 'boolean' },
|
||||
autoSensitive: { type: 'boolean' },
|
||||
ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
||||
pinnedPageId: { type: 'array', items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
@ -168,6 +170,7 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
||||
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
|
||||
if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
|
||||
if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive;
|
||||
if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes;
|
||||
|
||||
if (ps.avatarId) {
|
||||
|
Reference in New Issue
Block a user