Update content-moderation.js

This commit is contained in:
ErikrafT 2025-05-07 18:50:41 -03:00 committed by GitHub
parent 7e822acb45
commit b6dee67c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 55 additions and 72 deletions

View File

@ -96,16 +96,20 @@ class ContentModeration {
// Inicializa o modelo NSFW // Inicializa o modelo NSFW
this.nsfwModel = null; this.nsfwModel = null;
this.initNSFWModel(); this.nsfwModelLoading = false;
this.loadNSFWModel();
} }
async initNSFWModel() { async loadNSFWModel() {
if (this.nsfwModel || this.nsfwModelLoading) return;
this.nsfwModelLoading = true;
try { try {
// Carrega o modelo NSFW do TensorFlow.js this.nsfwModel = await nsfwjs.load();
this.nsfwModel = await tf.loadGraphModel('https://d1zv2aa70wpiur.cloudfront.net/tfjs_models/tfjs_nsfw_mobilenet/model.json'); console.log('Modelo NSFW carregado!');
} catch (error) { } catch (error) {
console.error('Erro ao carregar modelo NSFW:', error); console.error('Erro ao carregar modelo NSFW:', error);
} }
this.nsfwModelLoading = false;
} }
// Normaliza o texto removendo substituições de caracteres // Normaliza o texto removendo substituições de caracteres
@ -136,21 +140,16 @@ class ContentModeration {
// Verifica se o conteúdo é NSFW // Verifica se o conteúdo é NSFW
async checkNSFW(file) { async checkNSFW(file) {
if (!this.isMediaFile(file)) return false; if (!this.isMediaFile(file)) return false;
try { try {
// Verifica primeiro pelo nome do arquivo
const fileName = file.name.toLowerCase(); const fileName = file.name.toLowerCase();
if (this.blockedWords.some(word => fileName.includes(word.toLowerCase()))) { if (this.blockedWords.some(word => fileName.includes(word.toLowerCase()))) {
return true; return true;
} }
// Se for uma imagem ou vídeo, verifica o conteúdo
if (file.type.startsWith('image/')) { if (file.type.startsWith('image/')) {
return await this.checkImageNSFW(file); return await this.checkImageNSFW(file);
} else if (file.type.startsWith('video/')) { } else if (file.type.startsWith('video/')) {
return await this.checkVideoNSFW(file); return await this.checkVideoNSFW(file);
} }
return false; return false;
} catch (error) { } catch (error) {
console.error('Erro ao verificar NSFW:', error); console.error('Erro ao verificar NSFW:', error);
@ -159,75 +158,59 @@ class ContentModeration {
} }
async checkImageNSFW(file) { async checkImageNSFW(file) {
if (!this.nsfwModel) return false; if (!this.nsfwModel) await this.loadNSFWModel();
return new Promise((resolve) => {
try { const img = new window.Image();
const img = await createImageBitmap(file); img.onload = async () => {
const tensor = tf.browser.fromPixels(img) const predictions = await this.nsfwModel.classify(img);
.resizeBilinear([224, 224]) const isNSFW = predictions.some(p =>
.expandDims() (p.className === 'Porn' || p.className === 'Hentai' || p.className === 'Sexy') && p.probability > 0.7
.toFloat() );
.div(255.0); resolve(isNSFW);
};
const predictions = await this.nsfwModel.predict(tensor).data(); img.onerror = () => resolve(false);
tensor.dispose(); img.src = URL.createObjectURL(file);
});
// Verifica se o conteúdo é NSFW
const nsfwScore = predictions[1]; // Índice 1 é para conteúdo NSFW
return nsfwScore > 0.5; // Threshold de 50%
} catch (error) {
console.error('Erro ao verificar imagem NSFW:', error);
return false;
}
} }
async checkVideoNSFW(file) { async checkVideoNSFW(file) {
if (!this.nsfwModel) return false; if (!this.nsfwModel) await this.loadNSFWModel();
// Extrai frames do vídeo e analisa cada um
try {
const video = document.createElement('video');
video.src = URL.createObjectURL(file);
return new Promise((resolve) => { return new Promise((resolve) => {
const video = document.createElement('video');
video.preload = 'auto';
video.src = URL.createObjectURL(file);
video.muted = true;
video.currentTime = 0;
video.onloadeddata = async () => { video.onloadeddata = async () => {
// Captura frames do vídeo para análise
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = video.videoWidth; canvas.width = video.videoWidth;
canvas.height = video.videoHeight; canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
let nsfwDetected = false;
// Analisa alguns frames do vídeo let framesChecked = 0;
const frameCount = 5; const totalFrames = 5;
const interval = video.duration / frameCount; const duration = video.duration;
let nsfwFrames = 0; for (let i = 1; i <= totalFrames; i++) {
video.currentTime = (duration * i) / (totalFrames + 1);
for (let i = 0; i < frameCount; i++) {
video.currentTime = i * interval;
await new Promise(r => video.onseeked = r); await new Promise(r => video.onseeked = r);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0); const img = new window.Image();
const tensor = tf.browser.fromPixels(canvas) img.src = canvas.toDataURL();
.resizeBilinear([224, 224]) await new Promise(r => img.onload = r);
.expandDims() const predictions = await this.nsfwModel.classify(img);
.toFloat() if (predictions.some(p =>
.div(255.0); (p.className === 'Porn' || p.className === 'Hentai' || p.className === 'Sexy') && p.probability > 0.7
)) {
const predictions = await this.nsfwModel.predict(tensor).data(); nsfwDetected = true;
tensor.dispose(); break;
if (predictions[1] > 0.5) {
nsfwFrames++;
} }
framesChecked++;
} }
resolve(nsfwDetected);
URL.revokeObjectURL(video.src);
resolve(nsfwFrames > frameCount / 2); // Se mais da metade dos frames for NSFW
}; };
video.onerror = () => resolve(false);
}); });
} catch (error) {
console.error('Erro ao verificar vídeo NSFW:', error);
return false;
}
} }
// Verifica se uma URL contém caracteres cirílicos // Verifica se uma URL contém caracteres cirílicos