@danhachuel/leadmaster 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>LeadMaster — Painel</title>
8
- <script type="module" crossorigin src="/assets/index-CANBU7DH.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-eswbuT3S.css">
8
+ <script type="module" crossorigin src="/assets/index-CPq53JWx.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-CFQHp6yf.css">
10
10
  </head>
11
11
  <body class="bg-gray-100 min-h-screen">
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@danhachuel/leadmaster",
3
- "version": "0.1.00",
4
- "description": "SaaS local de prospecção e automação de conteúdo: CRM, extratores de leads, agentes de IA e pipeline de vídeos faceless (YouTube + Shorts).",
3
+ "version": "0.1.01",
4
+ "description": "SaaS local de prospecção e automação: CRM, extratores de leads e agentes de IA — 100% local.",
5
5
  "license": "MIT",
6
6
  "author": "Dan Hachuel",
7
7
  "type": "module",
@@ -24,16 +24,14 @@
24
24
  "install:local": "node scripts/cli.mjs install",
25
25
  "doctor": "node scripts/cli.mjs doctor",
26
26
  "dev": "concurrently -n api,web -c blue,magenta \"npm:dev:api\" \"npm:dev:web\"",
27
- "dev:api": "cd backend && python -m uvicorn main:app --reload --host 127.0.0.1 --port 8000",
27
+ "dev:api": "cd backend && python -m uvicorn main:app --reload --host 127.0.0.1 --port 8010",
28
28
  "dev:web": "npm --prefix frontend run dev",
29
29
  "build": "npm --prefix frontend run build"
30
30
  },
31
31
  "keywords": [
32
32
  "lead-generation",
33
33
  "crm",
34
- "faceless-video",
35
- "youtube",
36
- "shorts",
34
+ "prospection",
37
35
  "automation",
38
36
  "local-first"
39
37
  ],
package/scripts/cli.mjs CHANGED
@@ -301,7 +301,7 @@ function cmdInstall(args) {
301
301
  console.log('');
302
302
  ensureHome(home);
303
303
  console.log(`✓ Pasta de dados pronta: ${home}`);
304
- console.log(` (leads, campanhas, pipeline e configuração ficam em "${path.join(home, 'storage')}")`);
304
+ console.log(` (leads, campanhas e configuração ficam em "${path.join(home, 'storage')}")`);
305
305
 
306
306
  const python = detectPython();
307
307
  if (!python) {
@@ -1,102 +0,0 @@
1
- """Integração com o MoneyPrinterTurbo (produção de vídeos wide 16:9).
2
-
3
- API esperada (padrão do projeto): POST http://localhost:8080/api/v1/videos
4
- Cria a tarefa e consulta o progresso até o vídeo ficar pronto.
5
- """
6
-
7
- import time
8
-
9
- import httpx
10
-
11
- from . import storage
12
-
13
-
14
- class ServicoIndisponivel(Exception):
15
- """MoneyPrinterTurbo não está rodando — mensagem acionável."""
16
-
17
-
18
- class ProducaoErro(Exception):
19
- """O serviço respondeu, mas a produção falhou."""
20
-
21
-
22
- def _conf() -> dict:
23
- return storage.get_config()["integracoes"]["moneyprinter"]
24
-
25
-
26
- def _url() -> str:
27
- return _conf()["url"].rstrip("/")
28
-
29
-
30
- def verificar() -> bool:
31
- """True se o serviço responde (qualquer código HTTP conta como 'vivo')."""
32
- try:
33
- httpx.get(_url() + "/docs", timeout=2)
34
- return True
35
- except httpx.HTTPError:
36
- try:
37
- httpx.get(_url(), timeout=2)
38
- return True
39
- except httpx.HTTPError:
40
- return False
41
-
42
-
43
- def criar_video_wide(
44
- subject: str,
45
- script: str,
46
- terms: str = "",
47
- duracao_segundos: int = 60,
48
- voz: str = "pt-BR-Standard",
49
- ) -> str:
50
- """Cria a tarefa de vídeo wide (16:9). Retorna o task_id."""
51
- payload = {
52
- "video_subject": subject,
53
- "video_script": script,
54
- "video_terms": terms or subject,
55
- "video_aspect": "16:9",
56
- "video_clip_duration": min(duracao_segundos, 20),
57
- "video_source_provider": "pexels",
58
- "voice_volume": 1.0,
59
- "voice_rate": 1.0,
60
- }
61
- try:
62
- r = httpx.post(_url() + "/api/v1/videos", json=payload, timeout=120)
63
- except httpx.HTTPError as e:
64
- raise ServicoIndisponivel(
65
- f"MoneyPrinterTurbo não respondeu em {_url()}. "
66
- "Inicie o serviço (uvicorn app.server:app) e clique em Repetir."
67
- ) from e
68
- if r.status_code >= 400:
69
- raise ProducaoErro(f"MoneyPrinterTurbo rejeitou a tarefa (HTTP {r.status_code}): {r.text[:300]}")
70
- task_id = (r.json().get("data") or {}).get("task_id")
71
- if not task_id:
72
- raise ProducaoErro(f"Resposta sem task_id: {r.text[:300]}")
73
- return task_id
74
-
75
-
76
- def aguardar_video(task_id: str, timeout_min: int = 20, a_cada_progresso=None) -> dict:
77
- """Sonda a tarefa até concluir. Retorna {progresso, videos, saidas}."""
78
- limite = time.time() + timeout_min * 60
79
- while time.time() < limite:
80
- try:
81
- r = httpx.get(_url() + "/api/v1/tasks", params={"task_id": task_id}, timeout=15)
82
- except httpx.HTTPError as e:
83
- raise ServicoIndisponivel(
84
- f"MoneyPrinterTurbo sumiu durante a produção: {e}. Clique em Repetir com o serviço ativo."
85
- ) from e
86
- if r.status_code >= 500:
87
- time.sleep(5)
88
- continue
89
- data = (r.json() or {}).get("data") or {}
90
- task = data
91
- if isinstance(data.get("tasks"), list) and data["tasks"]:
92
- task = data["tasks"][0]
93
- progresso = task.get("progress") or 0
94
- if a_cada_progresso:
95
- a_cada_progresso(progresso)
96
- if task.get("videos"):
97
- return {"progresso": 100, "videos": task["videos"], "saidas": task.get("progress_details", {})}
98
- time.sleep(5)
99
- raise ProducaoErro(
100
- f"A tarefa {task_id} não ficou pronta em {timeout_min} minutos. "
101
- "Verifique o MoneyPrinterTurbo e use Repetir."
102
- )
@@ -1,62 +0,0 @@
1
- """Integração com o OpenMontage (CLI local, fallback de produção de vídeos).
2
-
3
- O caminho do executável é configurável em Ferramentas → OpenMontage.
4
- Como as flags do CLI podem variar entre versões, usamos um template de
5
- argumentos configurável com os placeholders {roteiro} e {saida}.
6
- """
7
-
8
- import subprocess
9
- from pathlib import Path
10
-
11
- from . import storage
12
-
13
-
14
- class IntegracaoNaoConfigurada(Exception):
15
- """Caminho do CLI não configurado — mensagem acionável."""
16
-
17
-
18
- class ExecucaoErro(Exception):
19
- """O CLI falhou; stderr vai na mensagem."""
20
-
21
-
22
- def _conf() -> dict:
23
- return storage.get_config()["integracoes"]["openmontage"]
24
-
25
-
26
- def disponivel() -> bool:
27
- cli = _conf().get("cli_path", "")
28
- return bool(cli) and Path(cli).exists()
29
-
30
-
31
- def produzir(job_id: str, roteiro_path: str) -> dict:
32
- """Executa o OpenMontage com o roteiro e devolve {video_path}."""
33
- conf = _conf()
34
- cli = conf.get("cli_path", "")
35
- if not cli or not Path(cli).exists():
36
- raise IntegracaoNaoConfigurada(
37
- "OpenMontage não configurado. Informe o caminho do executável em "
38
- "Ferramentas → OpenMontage (https://github.com/openmotional/OpenMontage) "
39
- "ou mantenha o MoneyPrinterTurbo ativo."
40
- )
41
- saida = storage.videos_dir() / job_id
42
- saida.mkdir(parents=True, exist_ok=True)
43
- try:
44
- resultado = subprocess.run(
45
- [cli, roteiro_path, "--output", str(saida)],
46
- capture_output=True,
47
- text=True,
48
- timeout=1800,
49
- shell=False,
50
- )
51
- except FileNotFoundError as e:
52
- raise IntegracaoNaoConfigurada(f"Não foi possível executar o CLI do OpenMontage: {e}") from e
53
- except subprocess.TimeoutExpired as e:
54
- raise ExecucaoErro("OpenMontage passou de 30 minutos sem concluir.") from e
55
- if resultado.returncode != 0:
56
- raise ExecucaoErro(f"OpenMontage terminou com código {resultado.returncode}: {resultado.stderr[-400:]}")
57
- mp4 = sorted(saida.glob("*.mp4"))
58
- return {
59
- "video_path": str(mp4[-1]) if mp4 else None,
60
- "pasta_saida": str(saida),
61
- "observacao": "" if mp4 else "Execução OK, mas nenhum .mp4 encontrado na pasta de saída.",
62
- }