# Implantação do Troço Play no cPanel

Este projeto é uma aplicação Flask executada pelo **Setup Python App / Passenger** do cPanel. A implantação não exige VPS, Docker, Gunicorn, PM2 ou processo residente separado. O Passenger mantém a aplicação web, o MySQL armazena os dados e os Cron Jobs do cPanel executam as tarefas recorrentes por HTTP.

> **Importante:** use uma hospedagem cPanel que ofereça Python App/Passenger, Python 3.10 ou superior, MySQL/MariaDB e possibilidade de configurar Cron Jobs. Hospedagens exclusivamente PHP não conseguem executar esta versão Flask sem uma camada Python.

## 1. Preparar o banco MySQL

No cPanel, abra **MySQL Databases**, crie um banco e um usuário e conceda todos os privilégios ao usuário. Anote os nomes completos, pois muitos provedores acrescentam automaticamente o prefixo da conta.

A URL deve seguir este formato:

```text
mysql+pymysql://USUARIO_CPANEL:SENHA@localhost/NOME_BANCO?charset=utf8mb4
```

O aplicativo cria as tabelas no primeiro carregamento. Como alternativa, importe `schema_mysql.sql` no phpMyAdmin antes de iniciar o app.

## 2. Enviar os arquivos

Envie o conteúdo do repositório para uma pasta fora do `public_html`, por exemplo:

```text
/home/SEU_USUARIO/troco_play/
```

Não envie os artefatos locais abaixo, pois são grandes, temporários ou desnecessários:

| Item | Motivo |
|---|---|
| `backup/` | Backups locais não devem ser publicados; use o backup do cPanel/MySQL. |
| `instance/troco_play.db*` | O ambiente de produção usa MySQL. |
| `canais.txt` | Será baixado pela tarefa `update-m3u`, se essa função estiver habilitada. |
| `__pycache__/`, `.idea/`, arquivos `*.apk` | Não são necessários para o servidor web. |

Mantenha `instance/` gravável pelo usuário do aplicativo, pois o sistema armazena `settings.json` nessa pasta. Se o provedor exigir, use permissões de diretório `755` e arquivos `644`, ajustando somente o diretório de dados conforme a política do host.

## 3. Criar o Python App / Passenger

No cPanel, abra **Setup Python App** e crie uma aplicação com as seguintes configurações:

| Campo | Valor |
|---|---|
| Python version | 3.10 ou superior |
| Application root | `/home/SEU_USUARIO/troco_play` |
| Application URL | Seu domínio ou subdomínio |
| Startup file | `passenger_wsgi.py` |
| Entry point | `application` |

Depois de criar o app, instale as dependências no virtualenv exibido pelo cPanel:

```bash
cd /home/SEU_USUARIO/troco_play
pip install -r requirements.txt
```

Se o cPanel oferecer a seção **Configuration files**, indique `requirements.txt` e reinicie a aplicação depois da instalação.

## 4. Configurar as variáveis de ambiente

Defina as variáveis na seção **Environment variables** do Python App. Não publique credenciais no GitHub e não deixe valores de produção dentro de `app.py`.

| Variável | Obrigatória | Exemplo/uso |
|---|---:|---|
| `SECRET_KEY` | Sim | Chave aleatória longa para sessões e tokens. |
| `DATABASE_URL` | Sim | URL MySQL mostrada na seção anterior. |
| `CPANEL_MODE` | Sim | `1`, desativa processos residentes. |
| `FLASK_SKIP_SCHEDULER` | Sim | `1`, mantém o APScheduler interno desligado. |
| `CRON_SECRET` | Sim | Chave longa usada pelos Cron Jobs HTTP. |
| `HTTPS_ENABLED` | Sim | `true`, ativa cookies seguros. |
| `ADMIN_EMAIL` | Recomendável | E-mail do primeiro administrador. |
| `ADMIN_PASSWORD` | Recomendável | Senha forte do primeiro administrador. |
| `C7_API_KEY` | Se houver pagamentos | Chave privada do gateway. |
| `C7_API_SECRET` | Se houver pagamentos | Segredo privado do gateway. |
| `C7_API` | Se houver pagamentos | URL da API, normalmente `https://api.carteirado7.com/v2`. |
| `FFMPEG_ENABLED` | Não | Mantenha `false` em hospedagem compartilhada. |
| `M3U_URL` / `EPG_URL` | Recomendado | Fonte legada do catálogo e da grade; também podem ser salvas pelo painel administrativo. |
| `M3U_READ_TIMEOUT` | Não | Timeout de leitura da M3U em segundos; use `900` ou mais para listas grandes. |
| `M3U_MIN_ENTRIES` | Não | Quantidade mínima de itens aceita antes de substituir o catálogo; padrão `10`. |
| `STREAM_USER_AGENT` | Não | User-Agent enviado à origem; o padrão é `VLC/3.0`. |
| `STREAM_REFERER` | Não | Preencha somente quando a origem exigir um Referer específico. |
| `HEALTH_MAX_WORKERS` | Não | `5` é um limite conservador para cPanel. |

O arquivo `.env.example` contém um modelo sem credenciais. Em produção, prefira as variáveis do próprio Setup Python App em vez de manter `.env` dentro da pasta do site.

### Múltiplas listas M3U do mesmo servidor

No painel, em **Config M3U**, cole uma URL por linha no campo **URLs M3U / Credenciais**. A primeira lista é usada para importar e atualizar o catálogo unificado de canais, filmes e séries. As demais listas são tratadas como credenciais alternativas do mesmo servidor; elas não criam duplicatas no catálogo.

Quando uma reprodução começa, o sistema escolhe uma credencial livre entre as sessões ativas e assina o stream para o proxy. A seleção fica vinculada à sessão ativa, é renovada pelo heartbeat e expira após o timeout de inatividade. O banco aplica uma restrição única ao índice da fonte para impedir que duas sessões reservem a mesma conta ao mesmo tempo. Se a origem retornar limite, bloqueio ou indisponibilidade (`401`, `403`, `404`, `406`, `410`, `429` ou `5xx`), o proxy tenta as próximas credenciais do pool de forma limitada. Se todas as contas estiverem ocupadas ou falharem, a reprodução é recusada em vez de criar duplicidade silenciosa.

Use somente listas que tenham o mesmo servidor, a mesma estrutura de caminhos e os mesmos IDs de canais, filmes e episódios. Se os IDs variarem entre contas, a troca automática não poderá garantir que o conteúdo escolhido seja o mesmo.

As URLs podem ser mantidas no painel administrativo, que grava o conjunto em `instance/settings.json`. Não publique esse arquivo nem as credenciais no repositório GitHub. A atualização automática continua sendo executada pelos Cron Jobs do cPanel, sem processo residente.

### Substituição segura e classificação

Cada execução baixa primeiro para um arquivo temporário. A resposta só é aceita se contiver o cabeçalho `#EXTM3U`, URLs HTTP(S) e pelo menos `M3U_MIN_ENTRIES` entradas. Assim, uma resposta vazia, HTML de bloqueio, erro do provedor ou download interrompido não apaga o catálogo vigente. Depois da validação, o sincronizador lê a lista inteira em uma única transação lógica, atualiza os itens mantidos, insere os novos e remove do banco os canais, filmes e episódios que não existem mais na fonte; os vínculos dependentes de EPG, favoritos, histórico e relatórios dos itens removidos também são limpos para evitar registros órfãos. Se ocorrer erro durante o processamento, a transação é revertida e o arquivo anterior é restaurado.

A classificação usa o caminho da URL como regra principal: `/live/` resulta em canal, `/movie/`, `/movies/`, `/vod/` ou `/film/` resulta em filme, e `/series/` resulta em série. O `group-title` é usado apenas como apoio quando a URL não informa o tipo. Portanto, grupos com nomes incorretos, como `FILMES E SÉRIES` em uma fonte `/live/`, não movem o conteúdo para a aba errada. Temporada e episódio são extraídos de padrões como `S01 E02`, `S1E2` e `1x02`.

## 5. Configurar os Cron Jobs

O scheduler interno fica desligado no modo cPanel. No menu **Cron Jobs**, crie chamadas HTTP usando `curl` ou `wget`. Substitua o domínio e o segredo pelos valores reais:

```bash
curl -fsS --max-time 1200 -H "X-Cron-Secret: SEU_CRON_SECRET" https://SEU_DOMINIO/cron/update-m3u >/dev/null
```

Configuração sugerida:

| Frequência | URL |
|---|---|
| A cada 15 minutos | `/cron/update-m3u` |
| A cada 30 minutos | `/cron/update-epg` |
| A cada 5 minutos | `/cron/check-payments` |
| Diariamente | `/cron/backup` |
| A cada 15 minutos | `/cron/cleanup` |
| A cada 30 minutos | `/cron/health` |

Exemplo de expressão para o Cron Jobs do cPanel:

```text
*/15 * * * * curl -fsS --max-time 1200 -H "X-Cron-Secret: SEU_CRON_SECRET" https://SEU_DOMINIO/cron/update-m3u >/dev/null 2>&1
```

Se o provedor bloquear `curl`, use:

```bash
wget -q -O - --timeout=1200 --header="X-Cron-Secret: SEU_CRON_SECRET" https://SEU_DOMINIO/cron/update-m3u >/dev/null
```

## 6. Migrar dados existentes

Para migrar o SQLite local para o MySQL de produção, faça a migração antes ou depois do primeiro acesso:

```bash
cd /home/SEU_USUARIO/troco_play
export DATABASE_URL='mysql+pymysql://USUARIO_CPANEL:SENHA@localhost/NOME_BANCO?charset=utf8mb4'
python3 migrate_to_mysql.py
```

O arquivo `instance/troco_play.db` precisa estar presente apenas durante a migração. Depois de validar os dados, remova o SQLite do servidor.

## 7. Streaming e FFmpeg

O player principal usa obrigatoriamente o endpoint `/proxy` para encaminhar streams HTTP/HLS/MPEG-TS. O token assinado contém a URL já associada à credencial escolhida, e playlists HLS filhas continuam sendo reescritas para o mesmo proxy. O navegador não acessa diretamente as URLs de origem e o fluxo não depende de FFmpeg local. A conversão de streams TS para HLS em `/live/<id>.m3u8` é opcional e permanece desativada por padrão no cPanel, porque hospedagens compartilhadas normalmente não permitem processos `ffmpeg` persistentes.

Para funcionar no cPanel, os canais devem possuir URLs HLS diretas (`.m3u8`) ou outro formato aceito pelo navegador/player. Caso o provedor disponibilize FFmpeg e permita processos, o recurso pode ser ativado explicitamente com `FFMPEG_ENABLED=true` e `FFMPEG_BINARY=/caminho/absoluto/ffmpeg`, mas isso não é necessário para o fluxo padrão e pode violar limites da hospedagem.

## 8. Testes pós-instalação

Depois de reiniciar o Python App:

1. Acesse a página inicial via HTTPS.
2. Faça login com o administrador configurado por `ADMIN_EMAIL` e `ADMIN_PASSWORD`.
3. Acesse o painel e confirme que planos, canais e configurações carregam.
4. Execute manualmente um endpoint cron com o header correto e confirme a resposta `OK`.
5. No painel M3U, salve a nova fonte e execute a atualização manual; confirme os totais de adicionados, atualizados e removidos.
6. Teste um item de cada caminho (`/live/`, `/movie/` e `/series/`) e confirme as abas correspondentes.
7. Teste um canal HLS direto e confirme que o player reproduz sem usar `/live/`.
8. Verifique os logs do Passenger e do Python App caso apareça erro 500.

## 9. Backup e limites

O endpoint `/cron/backup` preserva arquivos de configuração e a lista M3U quando aplicável. Para backup completo do banco MySQL, use o **Backup Wizard** do cPanel ou exporte o banco pelo phpMyAdmin; não dependa de cópias SQLite em produção. Para maior segurança, mantenha a pasta `backup/` fora do `public_html` ou bloqueie seu acesso público no servidor.

A aplicação foi ajustada para não iniciar APScheduler, workers persistentes ou FFmpeg local quando `CPANEL_MODE=1`. O limite de concorrência do health check também é configurável por `HEALTH_MAX_WORKERS`, reduzindo o risco de atingir limites de CPU, memória ou conexões do plano compartilhado.

## Categorias, reprodução contínua e recuperação de senha

Além de `group-title`, o parser aceita `#EXTGRP`, aspas simples, aspas duplas e atributos sem aspas. Quando a fonte omite totalmente categorias, ela usa `CANAIS`, `FILMES` e `SÉRIES`; o marcador `[24h]` é normalizado para `24H`. Gêneros ou emissoras que não aparecem na M3U não podem ser reconstruídos com segurança.

Os tokens de stream permanecem válidos por 24 horas. A sessão de dispositivo tolera até 300 segundos sem heartbeat (`STREAM_SESSION_IDLE_SECONDS=300`) e o player web/TV renova o token e reconecta automaticamente em falhas HLS transitórias.

Para recuperação de senha e alertas, configure no Setup Python App: `SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD`, `SMTP_FROM`, `SMTP_TLS` e `PUBLIC_BASE_URL`. O código tem seis dígitos, expira em 15 minutos, permite no máximo cinco tentativas e é invalidado após uso. As notificações permanecem no site mesmo quando o SMTP está indisponível. `EMAIL_LOGIN_NOTIFICATIONS=true` envia alerta de novo login; altere para `false` se não quiser mensagens de login por e-mail. Pagamento confirmado, comissão, saque e suporte já utilizam o mesmo mecanismo de notificação.

## Proteção contra abuso, robôs e uso indevido

A aplicação mantém CSRF nos formulários, limites de requisição para login, cadastro, recuperação de senha e proxy nativo, cookies HTTPOnly/SameSite/Secure em HTTPS, tamanho máximo de requisições, cabeçalhos defensivos, token assinado para reprodução, validação de host no proxy nativo e bloqueio de URL arbitrária sem token. Os endpoints de segmentos HLS autenticados não usam o rate limit geral, evitando que uma playlist legítima seja interrompida por excesso de segmentos; o acesso continua protegido por sessão e token.

DDoS volumétrico não pode ser neutralizado somente pelo Flask ou por uma hospedagem compartilhada. No cPanel, coloque o domínio atrás de um WAF/CDN, como Cloudflare, ative o modo de proteção, limite `/login`, `/register`, `/esqueci-senha`, `/redefinir-senha` e endpoints administrativos, e mantenha o proxy de mídia permitido apenas para usuários autenticados. Nunca exponha o painel administrativo sem HTTPS e senha forte.
