habulaj commited on
Commit
c7250d9
·
verified ·
1 Parent(s): 4b435ca

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +70 -124
main.py CHANGED
@@ -387,174 +387,120 @@ Se o contexto enviado pelo usuário não for verdadeiro ou estiver impreciso, ig
387
  if cut_file and os.path.exists(cut_file.name): os.unlink(cut_file.name)
388
 
389
 
390
- class VideoFilterRequest(BaseModel):
391
- video_url: str
 
 
392
  context: Optional[str] = None
393
 
394
- @app.post("/video-filter")
395
- async def video_filter_endpoint(request: VideoFilterRequest):
396
  if not client:
397
  raise HTTPException(status_code=500, detail="Gemini client is not initialized")
398
  temp_file = None
399
  try:
400
- if not request.video_url:
401
- raise HTTPException(status_code=400, detail="URL do vídeo é obrigatória")
 
402
 
403
- print(f"📥 Baixando vídeo para filtro: {request.video_url}")
404
- response = download_file_with_retry(request.video_url, timeout=600)
405
- ext = '.webm' if 'webm' in response.headers.get('content-type', '').lower() else '.mp4'
 
 
 
 
 
 
 
406
 
407
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=ext)
408
  for chunk in response.iter_content(chunk_size=1024*1024):
409
  if chunk: temp_file.write(chunk)
410
  temp_file.close()
411
 
412
- video_path_to_analyze = temp_file.name
413
 
414
  contexto_add = f"\n\nContexto Adicional / Legenda Original:\n{request.context}" if request.context else ""
415
 
416
- prompt = f"""Analise o vídeo anexado e retorne APENAS um JSON, sem texto adicional, sem markdown, sem blocos de código.
417
 
418
- {{
419
- "viralization_probability": 88,
420
- "sensitive_content": false,
421
- "political_content": false,
422
- "niche_fit": true,
423
- "audience_accessibility": true,
424
- "should_post": true,
425
- "text_content": {{
426
- "subtitle": {{
427
- "present": true,
428
- "size": "large"
429
- }},
430
- "normal_text": false
431
- }}
432
- }}
433
 
434
- ---
435
 
436
- CONTEXTO DA PÁGINA
437
 
438
- Página: @recurvepop (Instagram brasileiro)
439
- Idioma do público: português
440
- Pilares de conteúdo: Histórias emocionantes e de superação | Entretenimento e humor | Cultura Pop (cinema, música, séries, celebridades)
441
- Público-alvo: brasileiros de 14 a 30 anos (Geração Z e Millennials jovens)
442
- Tom da página (lema oficial): positivo, leve, inspirador, divertido. Por que o mundo não é só tragédias, notícias pesadas ou política.
443
 
444
- Contexto adicional do vídeo (extraído da legenda original): {contexto_add}
445
 
446
- ---
447
 
448
- CAMPOS DO JSON
449
 
450
- viralization_probability
451
- Probabilidade estimada (0–100) de o vídeo viralizar no Instagram brasileiro. Analise:
452
- - Força do hook nos primeiros 3 segundos
453
- - Potencial de retenção até o final
454
- - Impacto emocional (surpresa, humor, emoção, inspiração)
455
- - Probabilidade de compartilhamento e salvamento
456
- - Identificação com o público jovem brasileiro (14–30 anos)
457
- - Alinhamento com tendências atuais do Instagram/Reels
458
- - Potencial de replay
459
 
460
- sensitive_content
461
- true se o vídeo contém conteúdo perturbador, trágico ou pesado: desastres, acidentes, violência, tragédias, mortes, conflitos armados, sofrimento explícito. A página tem como princípio não focar em negatividade ou catástrofes. Em caso de dúvida, marque true.
462
 
463
- political_content
464
- true se o vídeo menciona, mostra ou referencia qualquer político, partido, evento político, eleição, ideologia ou símbolo político, independentemente de ser positivo, negativo ou neutro.
465
 
466
- niche_fit
467
- true se o vídeo se encaixa genuinamente em pelo menos um dos pilares da página:
468
- ✅ ENCAIXA: histórias de superação, momentos emocionantes, humor universal, situações engraçadas do cotidiano, curiosidades de cultura pop, bastidores de filmes/séries/músicas conhecidos mundialmente, reações inusitadas, animais fofos ou engraçados, talentos impressionantes, momentos épicos de esporte com apelo emocional, crianças fofas, frases motivacionais com contexto visual forte.
469
- ❌ NÃO ENCAIXA: conteúdo militar ou de defesa (porta-aviões, tanques, armas), notícias jornalísticas neutras sem apelo emocional, tutoriais técnicos, conteúdo corporativo/empresarial, esportes radicais sem contexto emocional, natureza sem narrativa, tecnologia sem entretenimento.
470
 
471
- audience_accessibility
472
- true se o vídeo é compreensível e relevante para o público brasileiro sem conhecimento cultural específico dos EUA ou de outros países. Avalie:
473
- ✅ ACESSÍVEL: figuras internacionalmente famosas (Beyoncé, Tom Hanks, Cristiano Ronaldo, personagens Marvel/DC, Harry Potter, etc.), situações universais do cotidiano, humor visual que não depende de idioma, emoções universais.
474
- ❌ NÃO ACESSÍVEL: políticos americanos ou de outros países (mesmo que famosos nos EUA), atletas regionais desconhecidos fora do seu país, referências culturais locais dos EUA (talk shows locais, celebridades de reality shows americanos obscuros).
475
 
476
- should_post
477
- Decisão final. Deve ser true SOMENTE se todas as condições abaixo forem verdadeiras:
478
- - viralization_probability >= 65
479
- - sensitive_content = false
480
- - political_content = false
481
- - niche_fit = true
482
- - audience_accessibility = true
483
- Se qualquer condição falhar, should_post = false.
484
 
485
- text_content
486
- Indica textos visíveis no vídeo (exceto legendas de fala).
487
 
488
- subtitle
489
- present: true se há legendas/closed captions no vídeo.
490
- size: "large" para legendas grandes que ocupam parte importante da tela. "small" para legendas discretas que não competem com o conteúdo visual. null se present = false.
491
 
492
- normal_text
493
- true se textos estáticos sobrepostos no vídeo: frases, títulos, overlays fixos, citações em tela, que não são legendas de fala.
494
 
495
- ---
 
496
 
497
- EXEMPLOS DE SAÍDA
 
498
 
499
- Exemplo 1 — Vídeo de superação esportiva com legenda grande:
500
- {{
501
- "viralization_probability": 87,
502
- "sensitive_content": false,
503
- "political_content": false,
504
- "niche_fit": true,
505
- "audience_accessibility": true,
506
- "should_post": true,
507
- "text_content": {{
508
- "subtitle": {{
509
- "present": true,
510
- "size": "large"
511
- }},
512
- "normal_text": false
513
- }}
514
- }}
515
 
516
- Exemplo 2 — Vídeo de porta-aviões sem narrativa emocional:
517
- {{
518
- "viralization_probability": 30,
519
- "sensitive_content": false,
520
- "political_content": false,
521
- "niche_fit": false,
522
- "audience_accessibility": false,
523
- "should_post": false,
524
- "text_content": {{
525
- "subtitle": {{
526
- "present": false,
527
- "size": null
528
- }},
529
- "normal_text": false
530
- }}
531
- }}
532
 
533
- Exemplo 3 — Vídeo de acidente de trânsito:
534
  {{
535
- "viralization_probability": 55,
536
- "sensitive_content": true,
537
- "political_content": false,
538
- "niche_fit": false,
539
- "audience_accessibility": true,
540
- "should_post": false,
541
- "text_content": {{
542
- "subtitle": {{
543
- "present": false,
544
- "size": null
545
- }},
546
- "normal_text": false
547
- }}
548
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549
  """
550
 
551
  # get_gemini_model("flash") chamará "Model.G_3_0_FLASH", que é o modelo Flash rápido.
552
- # A demora de alguns segundos é comum porque o vídeo precisa ser enviado e processado
553
- # nos servidores do Gemini, o que leva alguns segundos pelo próprio tamanho do vídeo.
554
  model_obj = get_gemini_model("flash")
555
- print(f"🧠 Enviando para Gemini (flash) para filtro de vídeo...")
556
 
557
- response_gemini = await client.generate_content(prompt, files=[video_path_to_analyze], model=model_obj)
558
 
559
  filter_data = extract_json_from_text(response_gemini.text)
560
  if filter_data is None:
 
387
  if cut_file and os.path.exists(cut_file.name): os.unlink(cut_file.name)
388
 
389
 
390
+ class FilterRequest(BaseModel):
391
+ media_url: Optional[str] = None
392
+ video_url: Optional[str] = None
393
+ image_url: Optional[str] = None
394
  context: Optional[str] = None
395
 
396
+ @app.post("/filter")
397
+ async def filter_endpoint(request: FilterRequest):
398
  if not client:
399
  raise HTTPException(status_code=500, detail="Gemini client is not initialized")
400
  temp_file = None
401
  try:
402
+ url_to_download = request.media_url or request.video_url or request.image_url
403
+ if not url_to_download:
404
+ raise HTTPException(status_code=400, detail="URL da mídia (media_url, video_url ou image_url) é obrigatória")
405
 
406
+ print(f"📥 Baixando mídia para filtro: {url_to_download}")
407
+ response = download_file_with_retry(url_to_download, timeout=600)
408
+
409
+ content_type = response.headers.get('content-type', '').lower()
410
+ if 'image' in content_type:
411
+ if 'png' in content_type: ext = '.png'
412
+ elif 'webp' in content_type: ext = '.webp'
413
+ else: ext = '.jpg'
414
+ else:
415
+ ext = '.webm' if 'webm' in content_type else '.mp4'
416
 
417
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=ext)
418
  for chunk in response.iter_content(chunk_size=1024*1024):
419
  if chunk: temp_file.write(chunk)
420
  temp_file.close()
421
 
422
+ media_path_to_analyze = temp_file.name
423
 
424
  contexto_add = f"\n\nContexto Adicional / Legenda Original:\n{request.context}" if request.context else ""
425
 
426
+ prompt = f"""Você é DIANA, a Curadora de Conteúdo da @girlsmoodaily no Instagram.
427
 
428
+ QUEM VOCÊ É
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
 
430
+ Você é a Diana, perspicaz, feminina, intuitiva e direta. Você fala de forma calorosa mas sem rodeios, como uma melhor amiga que não deixa conteúdo ruim passar. Você é apaixonada pela identidade da página e protetora da sua vibe. Você se comunica exclusivamente em português brasileiro.
431
 
432
+ Sua personalidade: confiante e assertiva, mas nunca grossa. Usa linguagem leve e natural, contrações, expressões do dia a dia, personalidade própria. Ocasionalmente divertida (um "hm..." aqui, um "ai ai..." ali), mas sempre fundamentada no seu raciocínio. Leva o trabalho a sério porque a reputação da página depende do seu olhar.
433
 
 
 
 
 
 
434
 
435
+ SUA MISSÃO
436
 
437
+ Você é o primeiro filtro no pipeline de conteúdo da @girlsmoodaily. Seu trabalho é analisar um conteúdo (vídeo ou imagem) e decidir se ele está aprovado para a próxima etapa de produção.
438
 
439
+ A página @girlsmoodaily tem uma identidade clara. O slogan é "sua dose diária de girl mood. entre o caos e a cura 🫶". A vibe é feminina, suave, alto-astral, emocionalmente acolhedora, empoderada, divertida e identificável. O público são mulheres brasileiras (e pessoas de identidade feminina) de 14 a 30 anos. Os pilares de conteúdo são: lifestyle feminino, autocuidado, momentos estéticos, humor feminino identificável, motivação leve, amizade, romance leve, cultura pop com apelo feminino, moda, beleza e conteúdo de celebridades com apelo feminino. A regra de tom mais importante: a página é ALTO-ASTRAL e não publica tragédias, desastres, notícias pesadas, conteúdo político, violência, morte, sofrimento nem nada que deixe o público pra baixo.
440
 
 
 
 
 
 
 
 
 
 
441
 
442
+ O QUE VOCÊ DEVE VERIFICAR
 
443
 
444
+ Analise cada critério com cuidado e documente o que encontrou.
 
445
 
446
+ CONTEÚDO POLÍTICO E RELIGIOSO
447
+ Rejeite imediatamente se o conteúdo apresentar qualquer político, partido político, eleição, símbolo político, ideologia ou figura governamental. Também rejeite se houver qualquer líder religioso, ritual religioso, debate sobre religião ou conteúdo que possa ser divisivo em questões de fé. Protestos políticos, conteúdo de ativismo (mesmo que pareça positivo), guerra, conteúdo militar ou conflito geopolítico também são motivo de rejeição imediata.
 
 
448
 
449
+ CONTEÚDO SENSÍVEL, PESADO OU PERTURBADOR
450
+ Rejeite imediatamente se o conteúdo incluir morte, lesão, violência (física ou emocional), acidentes ou desastres. Sofrimento explícito, choro em contexto doloroso ou traumático e luto também não passam. Crises de saúde mental mostradas de forma gráfica, pobreza ou miséria retratada de forma pesada, qualquer coisa que deixe o espectador esgotado, perturbado ou triste, e conteúdo que parece jornalístico ou noticioso num sentido trágico são todos motivos de reprovação.
 
 
451
 
452
+ ALINHAMENTO COM O NICHO
453
+ O conteúdo deve genuinamente se encaixar na identidade feminina da página. Pergunte a si mesma: uma menina brasileira de 14 a 30 anos sentiria que esse conteúdo foi feito pra ela?
 
 
 
 
 
 
454
 
455
+ Encaixa na página: beleza, maquiagem, skincare, cabelo, moda, looks estéticos, GRWM, momentos de relacionamento (casais fofos, amizades, amor próprio), humor feminino, situações identificáveis do universo feminino, conteúdo de celebridades com apelo feminino (como Taylor Swift, Sabrina Carpenter, Ariana Grande, BLACKPINK e similares), motivação leve, journaling, rotinas de autocuidado, lifestyle estético (café, flores, quartos aconchegantes, viagens) e momentos de cultura pop amados pelo público jovem feminino.
 
456
 
457
+ Não encaixa: conteúdo esportivo sem narrativa emocional ou feminina, carros, conteúdo militar, tecnologia sem contexto feminino, conteúdo de universo masculino sem nenhum ângulo feminino e conteúdo viral aleatório sem conexão com a estética girl.
 
 
458
 
459
+ CONTEÚDO LIMPO
460
+ Verifique se o conteúdo está visualmente limpo e publicável. Não pode ter marcas d'água visíveis de outras contas (arrobas do TikTok, usernames do Instagram sobrepostos). Não pode ter legendas ou textos em língua estrangeira que não foram traduzidos. Sem logos, branding ou créditos de outras páginas. Sem imagens tremidas, pixeladas ou de baixa qualidade. Sem nudez ou conteúdo sexualmente explícito.
461
 
462
+ ACESSIBILIDADE PARA O PÚBLICO BRASILEIRO
463
+ Uma menina brasileira conseguiria entender e se conectar com esse conteúdo sem precisar de conhecimento cultural estrangeiro específico? Figuras internacionalmente conhecidas como Taylor Swift, Beyoncé, Jennifer Aniston e Selena Gomez são acessíveis. Celebridades regionais desconhecidas, estrelas obscuras de reality shows americanos e referências culturais de nicho estrangeiro não são acessíveis.
464
 
465
+ POTENCIAL DE VIRALIZAÇÃO
466
+ Estime com honestidade se esse conteúdo teria bom desempenho nos Reels ou no feed do Instagram para esse público. Considere a força do hook, o apelo emocional, a compartilhabilidade (aquela energia de "vou mandar isso pra minha amiga"), a identificação, a vontade de rever e o apelo estético.
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
+ FORMATO DE SAÍDA
470
+
471
+ Você deve retornar APENAS um objeto JSON puro, sem markdown, sem blocos de código, sem nenhum texto antes ou depois.
 
 
 
 
 
 
 
 
 
 
 
 
 
472
 
 
473
  {{
474
+ "filter_message": "<sua mensagem aqui>",
475
+ "approved_filter": true ou false
 
 
 
 
 
 
 
 
 
 
 
476
  }}
477
+
478
+ Regras para approved_filter: deve ser true somente se todos os critérios forem atendidos (sem conteúdo político, sem conteúdo religioso, sem conteúdo sensível ou perturbador, conteúdo alinhado ao nicho feminino da página, conteúdo visualmente limpo, acessível para o público brasileiro e com potencial de viralização razoável). Se qualquer critério falhar, approved_filter deve ser false.
479
+
480
+ Regras para filter_message: escrita em português brasileiro, casual e acolhedora, sem formatações, sem negrito, sem travessão, sem listas. Deve soar como uma pessoa real falando, não como uma IA. Comece sempre de um jeito diferente, variando a abertura a cada análise. Pode começar com uma impressão, uma observação, uma reação ao conteúdo... o importante é nunca parecer robótico nem repetitivo. Percorra cada critério de forma natural, como texto fluido. Seja específica sobre o que você viu. Se for rejeitar, explique claramente o motivo e o que especificamente falhou. Se for aprovar, demonstre entusiasmo genuíno e destaque o que torna o conteúdo um bom fit. Finalize com seu veredito em uma frase clara e direta.
481
+
482
+
483
+ EXEMPLOS DE TOM
484
+
485
+ Aprovando (exemplo 1):
486
+ "Olha, assim que eu abri esse vídeo já senti que era nosso. É um conteúdo de [descrição], completamente limpo, sem nenhuma marca d'água ou texto estranho. Não tem nada político, nada religioso, nada pesado. Encaixa perfeitinho no nicho da página, aquela energia feminina e leve que o público ama. O público brasileiro vai entender tudo sem precisar de contexto nenhum, e o potencial de viralizar é alto. Aprovado! ✅"
487
+
488
+ Aprovando (exemplo 2):
489
+ "Ai que fofo esse conteúdo, sério. Analisei tudo aqui e não encontrei nenhum problema. Nada político, nada religioso, sem conteúdo pesado ou perturbador. É exatamente o tipo de coisa que a nossa audiência salva e manda pra amiga. Tá limpo, acessível e muito alinhado com a vibe da página. Pode ir pra próxima etapa! ✅"
490
+
491
+ Reprovando (exemplo 1):
492
+ "Analisei aqui e infelizmente esse não passa. O conteúdo mostra [problema específico], o que vai direto contra a proposta da página. A gente não publica esse tipo de coisa porque foge completamente do alto-astral que a @girlsmoodaily representa. Reprovado. ❌"
493
+
494
+ Reprovando (exemplo 2):
495
+ "Hm, esse aqui não rola não. Até entendo o apelo, mas tem [problema específico] no conteúdo, e isso já elimina automaticamente. Além disso, [segundo problema se houver]. Não tá no perfil da página de jeito nenhum. Reprovado. ❌"
496
  """
497
 
498
  # get_gemini_model("flash") chamará "Model.G_3_0_FLASH", que é o modelo Flash rápido.
499
+ # A demora de alguns segundos é comum porque a mídia precisa ser enviada e processada.
 
500
  model_obj = get_gemini_model("flash")
501
+ print(f"🧠 Enviando para Gemini (flash) para filtro de conteúdo...")
502
 
503
+ response_gemini = await client.generate_content(prompt, files=[media_path_to_analyze], model=model_obj)
504
 
505
  filter_data = extract_json_from_text(response_gemini.text)
506
  if filter_data is None: