Documentação oficial
Central de Ajuda
Tudo sobre o WatchVerse: como instalar, usar cada recurso, resolver problemas e entender a arquitetura.
O WatchVerse é um aplicativo Android que reúne rádio online, TV ao vivo, notícias, futebol, filmes, séries, animes, mangás e jogos em um só lugar. O acesso ao catálogo é liberado por uma conta Google (Gmail) e por um acesso ativo de 30 dias, vendido por US$ 1.
Esta Central de Ajuda é a documentação oficial. Ela é publicada em https://watchverse.fun/documentacao e também é aberta pelo botão Ajuda dentro do aplicativo, em Perfil → Ajuda.
Por onde começar
- Primeiros passos — instalar o APK, entrar com Google, confirmar o código de 8 dígitos e ativar o acesso.
- Navegação e perfil — abas, busca, favoritos, notificações, idioma e configurações.
- Catálogo e player — filmes, séries, animes, detalhes, trailers, provedores e retomada de reprodução.
- TV ao vivo e rádio — países, canais, saúde dos streams e estações de rádio.
- Notícias e futebol — resumos jornalísticos, tabelas, resultados e próximos jogos.
- Mangás e jogos — leitor de mangá, progresso, catálogo de jogos e mídia.
- Conta, acesso e privacidade — licença, renovação, aparelho vinculado, biometria e dados.
- Solução de problemas — erros comuns e o que fazer antes de pedir suporte.
Para quem desenvolve ou administra
- Arquitetura — stack, navegação, contexto, serviços e armazenamento.
- Ambiente e build — requisitos, variáveis, scripts, testes e geração do APK.
- Integrações — Firebase, TMDb, MangaDex, RAWG/Steam, notícias, rádio e pagamentos.
- API do Worker — rotas reais, autenticação, parâmetros e limites.
- Modelo de segurança — proteções do app e limites conhecidos.
- Histórico de versões — mudanças relevantes das versões 4.4.6 a 4.4.13.
O que o WatchVerse não faz
- Não hospeda filmes, séries, canais ou mangás. O app organiza catálogos e abre players/leitores de terceiros.
- Não vende assinatura recorrente: o acesso é uma compra única de 30 dias.
- Não funciona em iPhone nesta versão. O alvo é Android 10 ou superior.
- Não garante disponibilidade de conteúdo de terceiros. Catálogo, canais e capítulos podem mudar sem aviso.
Versão documentada
Esta documentação descreve o aplicativo 4.4.13 (versionCode 19) e o site publicado em watchverse.fun. Quando uma funcionalidade existe no código mas não é usada na versão atual, isso é indicado explicitamente no capítulo correspondente.
Usuário
Primeiros passos
Requisitos
| Item | Valor |
|---|---|
| Sistema | Android 10 (API 29) ou superior |
| Conexão | Internet obrigatória para login, catálogo, canais, notícias e pagamento |
| Conta | Google/Gmail verificada |
| Espaço | O APK atual tem cerca de 120 MB |
| Orientação | Retrato e paisagem (inclui tablets, Samsung DeX e TVs compatíveis) |
O WatchVerse não está disponível para iPhone nesta versão. Em aparelhos sem os serviços do Google Play, o login pode não funcionar.
1. Baixar e instalar
- Abra https://watchverse.fun e toque em Baixar APK para Android.
- Confira o SHA-256 exibido na página de download, se quiser validar o arquivo. O site publica também o arquivo
.sha256. - Ao abrir o APK, o Android pode pedir permissão para instalar aplicativos de fonte desconhecida. Autorize apenas para o navegador ou gerenciador de arquivos usado no download.
- Conclua a instalação e abra o WatchVerse.
O aplicativo não pede permissão de câmera, armazenamento, mídia ou notificações. Essas permissões foram removidas do manifesto.
2. Entrar com Google
Na primeira abertura, o app mostra a tela de login. Toque em Continuar com Google e escolha uma conta Gmail.
- Contas que não sejam Gmail são recusadas.
- O login usa o Google Sign-In e o Firebase Authentication.
- A conta fica vinculada a um único aparelho. Se a mesma conta for usada em outro aparelho, o acesso é bloqueado com a mensagem de aparelho não autorizado.
3. Confirmar o código de 8 dígitos
Em alguns casos o Worker exige uma verificação adicional por e-mail. O app abre a tela de código.
| Regra | Valor |
|---|---|
| Formato | 8 dígitos |
| Validade | 5 minutos |
| Tentativas | 3 por código |
| Reenvio | A partir de 60 segundos |
| Limite | Até 5 códigos por hora por conta |
Digite o código recebido no e-mail da conta. Se ele expirar, peça outro. Verifique também a pasta de spam.
Depois da confirmação, o app guarda um token de sessão no armazenamento seguro do Android (SecureStore). Esse token é exigido pelas rotas de acesso e de conteúdo pago.
4. Ativar o acesso
Se a conta ainda não tiver acesso ativo, o app abre a tela Ative seu acesso.
- Valor: US$ 1 por 30 dias.
- Pagamento único com cartão de crédito ou débito, processado pelo Stripe.
- Depois de pagar, o app verifica o status automaticamente por até 30 segundos (15 tentativas a cada 2 segundos).
- Se o pagamento já tiver sido feito, use Já paguei, verificar.
- A tela também permite Sair da conta.
Quando o acesso é confirmado, o app libera a navegação principal. O status, a data de expiração e a chave de ativação (quando existir) aparecem em Perfil.
5. Renovar
O acesso não renova sozinho. Quando expirar, o app volta à tela de ativação e o cartão de acesso no Perfil mostra Expirado.
- Abra Perfil.
- No cartão de acesso, toque em Renovar (ou Pagar 1 USD).
- Conclua o pagamento e use Já paguei, verificar se necessário.
Primeira execução: o que acontece
- A splash nativa é exibida.
- O app executa as verificações de integridade em produção (ambiente de desenvolvimento, emulador, root e assinatura).
- Uma animação curta da marca é exibida.
- O Firebase, os provedores de player e o catálogo são inicializados.
- Se a inicialização falhar, aparece uma tela de erro com opção de tentar novamente.
Em produção, o app bloqueia emuladores, aparelhos com root e builds com assinatura diferente da oficial. A captura de tela também é prevenida enquanto o app está em uso.
Atualizações
A versão atual não executa atualização OTA dentro do app. Para atualizar, baixe o APK mais recente no site oficial e instale sobre a versão existente. A assinatura precisa ser a mesma; por isso, não instale APKs de outras fontes.
Se algo der errado
- Login recusado: confirme que a conta é Gmail e que os serviços do Google Play estão disponíveis.
- Código não chega: aguarde 60 segundos, confira spam e tente novamente dentro do limite de 5 por hora.
- Pagamento não libera: aguarde alguns instantes e toque em Já paguei, verificar. Se persistir, use o contato do site.
- Aparelho não autorizado: a conta já está vinculada a outro aparelho. O suporte precisa liberar a troca.
- Erro de conexão: veja Solução de problemas.
Usuário
Catálogo e player
De onde vêm os dados
Filmes, séries, animes e trailers usam o TMDb (The Movie Database), acessado por meio do Worker do WatchVerse. O app envia o idioma escolhido (pt-BR ou en-US) e a região correspondente. Títulos, sinopses e imagens podem variar conforme o idioma.
O aplicativo não hospeda vídeos. Ele monta a ficha do título, permite escolher um provedor disponível e abre o player externo dentro de uma WebView.
Filmes, séries e animes
As três telas usam grade com rolagem infinita:
- Filmes — populares e descobertas por filtros.
- Séries — populares e descobertas por filtros.
- Animes — séries de animação com idioma original japonês.
Cada tela tem:
- carregamento inicial com esqueletos;
- mensagem de erro com Tentar novamente;
- carregamento de páginas adicionais ao chegar ao fim;
- layout responsivo para celular, tablet e DeX.
Tela de detalhes
Ao tocar em um título, a tela de detalhes mostra:
- pôster, imagem de fundo, título, ano, nota e sinopse;
- gêneros e elenco;
- trailer do YouTube, quando disponível;
- temporadas e episódios (séries e animes);
- botão de favorito;
- lista de provedores/players disponíveis;
- botão para continuar de onde parou, quando há progresso salvo.
Trailers
O trailer é aberto em um modal com o player do YouTube. Se o vídeo não existir ou não puder ser carregado, o app mostra uma mensagem e mantém o restante da ficha funcional.
Provedores
A lista de provedores vem do Firebase Realtime Database (streaming_providers) e pode incluir links pessoais (personal_links). Cada provedor informa nome, tipo, ícone, prioridade e um modelo de URL.
O app:
- seleciona os provedores compatíveis com o tipo do título;
- monta a URL de embed com o ID do TMDb, temporada e episódio;
- aceita apenas URLs HTTPS;
- abre o player interno com uma lista restrita de hosts permitidos.
Se nenhum provedor carregar, a tela mostra um aviso e oferece tentar novamente. A disponibilidade dos provedores é externa e pode mudar sem aviso.
Player
O player interno usa react-native-webview e aplica estas regras:
| Regra | Comportamento |
|---|---|
| Protocolo | Somente HTTPS |
| Hosts | Whitelist do provedor + domínios do Google necessários ao embed |
| Google Drive | Links de arquivo são convertidos para o modo /preview |
| Playerflix / 2Embed | Apenas caminhos de embed reconhecidos |
| Orientação | Paisagem imersiva por padrão; botão para alternar |
| Tela | Mantém o aparelho acordado durante a reprodução |
| Captura de tela | Bloqueada em produção |
O botão X fecha o player. O botão de rotação alterna entre paisagem e retrato.
Progresso e “Continuar assistindo”
O progresso é salvo:
- a cada 15 segundos;
- quando o app vai para segundo plano;
- quando o player é fechado;
- quando a página envia a posição real do vídeo.
O app guarda a posição em segundos, a duração e o percentual. Ao reabrir, retoma a posição salva quando ela estiver mais de 20 segundos à frente do início.
O progresso fica no aparelho e, com login, também é sincronizado na conta. É isso que alimenta o trilho Continuar assistindo da Home.
Limitações reais
- O catálogo do TMDb pode estar incompleto ou desatualizado em relação a outras fontes.
- Alguns títulos não têm trailer, elenco ou provedor disponível.
- Um provedor pode sair do ar, exigir login próprio ou bloquear a região. O WatchVerse não controla esses serviços.
- O player depende das permissões do site incorporado. Se o provedor bloquear incorporação, o vídeo não abre mesmo com o link correto.
- O app não baixa filmes ou séries para assistir offline.
Favoritos e conta
Favoritos de filmes e séries são gravados por conta. Sem login, o app usa o armazenamento local. Ao entrar em outro aparelho com a mesma conta, os favoritos sincronizados aparecem na aba Favoritos.
Privacidade da reprodução
O WatchVerse não recebe o conteúdo do vídeo: a WebView carrega o site do provedor. O provedor pode coletar dados conforme as próprias políticas. O progresso salvo pelo WatchVerse contém apenas ID, título, tipo, posição e metadados do item.
Usuário
TV ao vivo e rádio
TV ao vivo
A TV usa listas públicas de canais (iptv-org) e listas de reserva mantidas pelo app. A tela permite:
- escolher o país nas abas horizontais;
- buscar por nome, categoria ou grupo;
- ver o indicador de saúde do canal (online, lento, offline ou verificando);
- favoritar canais;
- abrir o canal no player interno.
Países disponíveis
Brasil, Estados Unidos, Espanha, Argentina, México, Colômbia, Chile, Peru, Portugal, Itália, França, Alemanha, Reino Unido, Canadá, Japão, Coreia do Sul e Uruguai. A opção Todos reúne os países.
Como os canais são carregados
- O app baixa a lista M3U do país.
- Aplica a blocklist pública do iptv-org.
- Remove canais HTTP; somente HTTPS é aceito.
- Mescla canais de reserva para Brasil, Espanha e Estados Unidos.
- Guarda o resultado em cache por 24 horas.
- Verifica a saúde dos streams e guarda o resultado por 12 horas.
- Canais marcados como offline três vezes seguidas deixam de aparecer na lista padrão.
O indicador de saúde é uma estimativa: ele testa a resposta HTTP do stream, não a qualidade real do vídeo.
Player de TV
O player usa expo-video com suporte a HLS. Ele oferece:
- carregamento/buffering;
- mensagem de erro quando o canal não abre;
- ajuste de enquadramento (preencher a tela ou esticar);
- botão para abrir o rádio;
- reprodução em retrato ou paisagem.
Alguns canais exigem cabeçalhos Referer/User-Agent específicos. O app repassa esses cabeçalhos quando a lista os informa.
Limitações
- Canais podem sair do ar, mudar de URL ou bloquear a região.
- Canais privados/geobloqueados não são contornados pelo app.
- A disponibilidade varia por país e provedor.
- O WatchVerse não hospeda nem redistribui os streams.
- O modo de catálogo via Worker (
/api/iptv/*, com URLs assinadas) existe no código, mas é opcional e desligado por padrão. Na configuração padrão, o app usa as listas públicas.
Rádio
A rádio usa o Radio Browser, consultado pelo Worker. A tela permite:
- escolher o país nas abas (as mesmas da TV, incluindo Todos);
- buscar por nome, país, gênero ou idioma;
- favoritar estações;
- abrir o player de rádio.
O Worker retorna até 100 estações por país, ocultando estações quebradas e ordenando por popularidade. A lista é normalizada e duplicatas de URL são removidas.
Player de rádio
- Reprodução em segundo plano e controles na tela de bloqueio (título, artista e capa quando disponíveis).
- Botões de play/pause, reconectar e favoritar.
- Indicador “Ao vivo”.
- Reconexão automática com aviso quando o stream cai.
- Player dedicado com barras de “now playing”.
Espectro de áudio (limitado)
O player pode exibir um espectro de áudio, mas o recurso depende da permissão de gravação de áudio do Android. O APK atual não declara RECORD_AUDIO, então o espectro tende a aparecer como indisponível ou exigir uma permissão que o sistema não concede. A reprodução do rádio continua funcionando normalmente.
Limitações
- Estações podem estar offline ou mudar de URL.
- A ordem e a disponibilidade vêm do Radio Browser e podem variar.
- Streams HTTP são recusados pelo app.
- Não há download de estações para ouvir offline.
Usuário
Notícias e futebol
Notícias
A seção Notícias reúne manchetes por país, com resumo e link para a fonte original.
Países
Brasil, Estados Unidos, Reino Unido, França, Alemanha, Espanha, Itália, Portugal, Japão e China.
Como funciona
- O Worker coleta manchetes e conteúdo de fontes públicas.
- O texto é resumido automaticamente e limitado a 3.000 caracteres.
- O resumo, a imagem e os metadados ficam no banco D1 do Worker.
- O app pede 20 artigos por página e mostra os mais recentes.
- Ao abrir um artigo, o app carrega o resumo e a mídia disponível.
O Worker atualiza um país a cada 20 minutos, em rodízio. Os artigos são mantidos para o dia local do país e limpos depois.
O que aparece no app
- título, fonte, país e horário;
- selo de “recente” para itens das últimas 6 horas;
- imagem de capa, quando disponível;
- resumo em português ou no idioma da fonte, conforme o país;
- até 4 itens de mídia (imagens ou vídeo) quando a fonte e a licença permitem;
- botão para abrir a notícia original.
Licenças e créditos
O Worker só aceita mídia com licença compatível (por exemplo, Creative Commons CC BY ou CC BY-SA) e mantém o nome e o link da licença. O artigo no app exibe a fonte e o crédito. Se a licença não for reconhecida, a mídia é descartada.
Avisos importantes
- O resumo é gerado automaticamente e pode conter imprecisões. Confira sempre a fonte original.
- O WatchVerse não é o autor da notícia e não controla o conteúdo de terceiros.
- Links externos são abertos no navegador do aparelho.
- Não há leitura offline nem armazenamento permanente dos artigos.
Futebol
A seção Futebol mostra dados de oito competições, obtidos do football-data.org v4 por meio do Worker.
| País/região | Competição |
|---|---|
| Brasil | Campeonato Brasileiro Série A |
| Itália | Serie A |
| Alemanha | Bundesliga |
| Espanha | La Liga (Primera Division) |
| Inglaterra | Premier League |
| França | Ligue 1 |
| Portugal | Primeira Liga |
| Países Baixos | Eredivisie |
Abas
- Classificação — posição, time, pontos, jogos, vitórias, empates, derrotas, gols pró/contra e saldo.
- Resultados — partidas encerradas, com placar e rodada.
- Próximos jogos — partidas futuras, com data, hora e local quando disponíveis.
Atualização e cache
- O Worker guarda os dados em cache por cerca de 5 minutos.
- A tela permite atualizar manualmente.
- Quando a atualização falha, o app pode mostrar dados antigos com o aviso de cache/instabilidade.
- Partidas em andamento recebem o selo AO VIVO quando a fonte informa esse estado.
Limitações
- O futebol é somente informativo: não há transmissão de jogos na seção.
- A fonte pode atrasar placares ou não cobrir competições menores.
- Escudos e nomes de times dependem do retorno da API.
- A classificação pode aparecer incompleta em períodos de atualização da fonte.
Usuário
Mangás e jogos
Mangás
Os mangás vêm da MangaDex. O app consulta a API diretamente; no site, o mesmo catálogo usa um proxy Netlify para evitar CORS.
Catálogo
- grade com 3 colunas e 24 títulos por página;
- rolagem infinita;
- busca com atraso de digitação;
- filtro por idioma de tradução;
- apenas títulos com classificação safe são solicitados à MangaDex.
Idiomas disponíveis no filtro: todos, português (BR), inglês, espanhol, francês, italiano, alemão, russo, indonésio, polonês, vietnamita, turco, árabe, chinês, japonês e coreano.
Detalhes do mangá
A tela mostra capa, título, ano, status, autores, descrição e a lista de capítulos. É possível:
- filtrar capítulos por idioma;
- continuar do capítulo salvo;
- abrir qualquer capítulo no leitor.
Os capítulos buscados incluem, por padrão, português (BR) e inglês, além do idioma selecionado no catálogo. Isso evita que um mangá pareça “sem capítulos” quando o filtro está em outro idioma.
Leitor
- leitura vertical contínua;
- contador de páginas;
- pré-carregamento das duas próximas páginas;
- navegação para o capítulo anterior/próximo;
- tela mantida acesa durante a leitura;
- modo imersivo (barra de status oculta).
O progresso é salvo no aparelho por mangá, com o ID do capítulo, o rótulo, o idioma e a data. A chave usada é wv_manga_progress_v1, a mesma do leitor do site, o que facilita exportar/importar no futuro.
Limitações
- A disponibilidade de capítulos depende da MangaDex e dos grupos de scanlation.
- Não há download para leitura offline.
- O progresso de mangá é local; não sincroniza entre aparelhos.
- Conteúdo adulto não é solicitado pelo app (
contentRating=safe).
Jogos
O catálogo de jogos usa RAWG, com enriquecimento opcional de descrições e trailers da Steam, tudo por meio do Worker.
Catálogo
- ordenação por populares, recentes ou futuros;
- filtro por gênero;
- busca por nome;
- 30 jogos por página, em grade de 2 colunas;
- cache local de 10 minutos para listas e 6 horas para detalhes.
Detalhes do jogo
A ficha pode mostrar:
- capa, imagem de fundo, ano e nota;
- nota Metacritic, tempo médio de jogo e plataformas;
- descrição (RAWG ou Steam, conforme disponibilidade);
- gêneros, desenvolvedoras, publicadoras e site oficial;
- capturas de tela em visualizador com zoom;
- trailers da Steam;
- botão para ouvir a descrição por voz (TTS do aparelho).
A tela Mídia reúne capturas e trailers em uma grade dedicada.
Limitações
- O WatchVerse não vende, baixa ou executa jogos. É um catálogo informativo.
- Trailers só aparecem quando o Worker consegue enriquecer o jogo com dados da Steam.
- Descrições e imagens podem variar conforme o idioma e a fonte.
- Alguns jogos não têm capturas, trailers ou descrição.
- O TTS depende das vozes instaladas no Android; se não houver voz para o idioma, o app avisa.
Usuário
Conta, acesso e privacidade
Conta
O WatchVerse usa apenas login Google. A conta precisa ser Gmail verificada. O perfil exibe nome, e-mail e foto do Google; o avatar pode ser trocado por uma opção pronta ou por uma imagem da galeria.
A conta é vinculada a um aparelho. O identificador usado é o Android ID (SSAID). Se a conta for aberta em outro aparelho, o Worker retorna DEVICE_MISMATCH e o app bloqueia o acesso até que o suporte libere a troca.
Licença
| Item | Valor |
|---|---|
| Preço | US$ 1 |
| Duração | 30 dias |
| Cobrança | Pagamento único com cartão (Stripe) |
| Renovação | Manual; não há débito automático |
| Status possíveis | ACTIVE, EXPIRED, NONE, DEVICE_MISMATCH, OTP_REQUIRED |
O cartão de acesso em Perfil mostra:
- status atual e dias restantes;
- data de expiração;
- chave de ativação, quando o backend fornece uma;
- botão de renovação;
- botão de atualizar o status.
Como o status é verificado
O app chama GET /access/status com o token do Firebase, o ID do aparelho e o token de sessão. O Worker responde com o status, a expiração e os dias restantes.
ACTIVE: libera o app.EXPIREDouNONE: abre a tela de pagamento.OTP_REQUIRED: abre a verificação por código.DEVICE_MISMATCH: bloqueia e orienta a falar com o suporte.ERROR: mostra erro de conexão com opção de tentar novamente ou sair.
Pagamento
O app cria uma sessão de checkout no Worker e abre a página do Stripe em uma WebView. Quando o pagamento é concluído, o Stripe chama o webhook do Worker, que grava o acesso no Firebase. O app consulta o status por até 30 segundos.
Se o pagamento foi feito mas o acesso não apareceu, use Já paguei, verificar. Não pague duas vezes sem antes verificar o status.
Biometria
A biometria é opcional e configurada em Perfil → Configurações. Ela protege a sessão já autenticada; não substitui o login Google.
- Requer biometria forte cadastrada no Android.
- A preferência é salva no armazenamento seguro.
- Se a autenticação falhar, o app oferece sair da conta.
- Em aparelhos sem biometria compatível, a opção não funciona.
Sair da conta
Perfil → Sair encerra a sessão do Firebase, revoga o token de sessão no Worker quando possível e limpa o token local. Favoritos e progresso locais permanecem no aparelho; os dados sincronizados continuam na conta.
Dados coletados
O WatchVerse coleta o mínimo necessário para autenticar, liberar o acesso e manter o serviço. De forma geral:
| Dado | Finalidade |
|---|---|
| Nome, e-mail e foto do Google | Perfil e identificação da conta |
| UID do Firebase | Vincular favoritos, progresso e licença |
| Android ID (SSAID) | Vincular a conta a um aparelho |
| Modelo, marca, versão do Android e versão do app | Diagnóstico e registro do dispositivo |
| Endereço IP (hash) e país/região/cidade aproximados | Segurança, antifraude e estatísticas |
| Status da licença e expiração | Liberar o acesso |
| Favoritos e progresso de reprodução/leitura | Sincronizar a experiência |
| Estado de leitura das notificações | Contador de não lidas |
| Identificador de instalação de anúncios | Medir impressões e cliques |
| Eventos de anúncio (impressão, clique, fechamento) | Relatório de campanhas |
| Diagnósticos de erro (Sentry) | Corrigir falhas |
O app não pede câmera, armazenamento, mídia ou permissão de notificações. O backup do aplicativo está desativado (allowBackup=false).
Armazenamento local
- AsyncStorage: favoritos locais, progresso, preferências da Home, cache de catálogo, avatar escolhido, notificações lidas e cache do banner.
- SecureStore: token de sessão, preferência de biometria, estado de licença e idioma.
Nada disso é acessível a outros aplicativos em um aparelho sem root.
Anúncios
O banner é carregado do Firebase Realtime Database e exibido após 60 segundos de uso para contas ativas. O app envia eventos de impressão, clique e fechamento ao Worker, com identificador de instalação, versão do app e dados aproximados de região. O conteúdo do anúncio é HTML fornecido pelo backend; ao tocar em um link, o app valida o URL e abre no navegador.
Segurança do aplicativo
Em builds de produção:
- emuladores, root e ambiente de desenvolvimento são bloqueados;
- a assinatura do APK é verificada;
- a captura de tela é prevenida;
- todo tráfego é HTTPS (cleartext bloqueado);
- o app não permite backup dos dados;
- WebViews de player e anúncio aplicam whitelist de hosts e bloqueiam navegação insegura.
Privacidade e termos
- Política de Privacidade: https://watchverse.fun/privacidade.html
- Termos de Uso: https://watchverse.fun/termos.html
- Contato: https://watchverse.fun/contato.html
Para solicitar exclusão de dados ou troca de aparelho, use o formulário de contato com o e-mail da conta. Não há exclusão automática de conta dentro do app nesta versão.
Usuário
Solução de problemas
Antes de pedir suporte
- Confirme que está usando o APK oficial de watchverse.fun.
- Verifique a conexão com a internet.
- Feche e abra o app novamente.
- Em Perfil, toque no botão de atualizar o status do acesso.
- Se o problema for em um conteúdo específico, teste outro título/canal para separar falha do app de falha do provedor.
Instalação
“App não instalado” ou “pacote inválido”
- Baixe novamente o APK; o arquivo pode ter chegado incompleto.
- Confira o SHA-256 publicado no site.
- Desinstale uma versão de outra origem antes de instalar a oficial. Assinaturas diferentes não podem coexistir.
- Verifique se há espaço livre suficiente (cerca de 120 MB para o APK, mais o espaço de instalação).
Android bloqueia a instalação
- Autorize “instalar apps desconhecidos” para o navegador ou gerenciador de arquivos usado.
- Em Android 14+, pode ser necessário liberar o instalador de pacotes nas configurações.
App fecha ao abrir
- Em produção, o app bloqueia emuladores, root e builds com assinatura diferente.
- Se aparecer a tela de segurança, use um aparelho sem root e o APK oficial.
- Reinicie o aparelho e tente novamente.
Login
“Entre com uma conta Gmail”
- O WatchVerse só aceita contas Gmail. Contas corporativas ou de outros domínios são recusadas.
O Google não abre ou falha
- Atualize os serviços do Google Play.
- Verifique se há uma conta Google configurada no aparelho.
- Teste outra rede.
- Se o erro persistir, saia e tente novamente.
A conta funciona em outro aparelho, mas não neste
- A conta já está vinculada a outro aparelho. Use o contato do site para solicitar a troca. O app não permite desvincular sozinho.
Código de verificação
O código não chega
- Aguarde até 60 segundos antes de pedir outro.
- Confira a pasta de spam/lixo eletrônico.
- Confirme que o e-mail é o mesmo da conta Google.
- Há limite de 5 códigos por hora.
“Código expirado”
- Peça um novo código. A validade é de 5 minutos.
“Muitas tentativas”
- O código permite 3 tentativas. Peça outro e digite com cuidado.
- Se o limite por hora foi atingido, aguarde antes de tentar novamente.
Pagamento e acesso
Paguei, mas o app continua pedindo pagamento
- Aguarde alguns instantes e toque em Já paguei, verificar.
- Confirme se o pagamento foi aprovado no cartão.
- Verifique se o e-mail da compra é o mesmo da conta logada.
- Se o cartão foi recusado, tente outro meio de pagamento.
“Aparelho não autorizado”
- A conta está vinculada a outro aparelho. Solicite a liberação pelo contato.
“Não foi possível verificar seu acesso”
- É falha de conexão ou instabilidade do Worker. Toque em Tentar novamente.
- Se persistir, saia e entre novamente.
- Em último caso, use o contato.
O acesso expirou antes do esperado
- O cartão do Perfil mostra a data real de expiração.
- Renove normalmente; não há renovação automática.
TV ao vivo
O canal não abre
- Teste outro canal do mesmo país.
- O canal pode estar offline ou geobloqueado.
- O indicador de saúde pode estar desatualizado; atualize a lista.
- Canais que falharam três vezes podem sumir da lista padrão.
A lista está vazia
- Troque o país e volte.
- Puxe para atualizar.
- Verifique a conexão. A lista é cacheada por 24 horas.
O vídeo trava ou fica em buffering
- Teste outra rede (Wi-Fi/dados).
- Feche outros apps que usam banda.
- Alguns canais são naturalmente instáveis; isso não é controlado pelo WatchVerse.
Rádio
Não toca
- Toque em Reconectar.
- Teste outra estação.
- Verifique se o stream está disponível no seu país.
- Confirme que o volume de mídia está alto.
Para ao bloquear a tela
- O app suporta reprodução em segundo plano, mas o Android pode encerrar o processo em aparelhos com economia de bateria agressiva. Libere o WatchVerse nas configurações de bateria.
O espectro de áudio não aparece
- Esse recurso depende de permissão de gravação de áudio, que o APK atual não declara. A rádio funciona sem o espectro.
Player de filmes e séries
Tela preta ou “link inválido”
- O provedor pode ter bloqueado incorporação ou saído do ar.
- Volte aos detalhes e escolha outro provedor.
- Teste outro título para confirmar se o problema é do provedor.
O vídeo não retoma de onde parei
- O progresso é salvo a cada 15 segundos e ao sair do player. Se o app foi encerrado à força, o último trecho pode ser perdido.
- A retomada só ocorre quando a diferença é maior que 20 segundos.
O player abre em paisagem e não volta
- Use o botão de rotação no canto superior direito.
- Saia do player e abra novamente.
Notícias e futebol
Notícias vazias
- Troque o país.
- Puxe para atualizar.
- O Worker atualiza um país a cada 20 minutos; pode haver atraso.
- Se a fonte não tiver artigos válidos no dia, a lista pode ficar vazia.
Classificação ou placar desatualizado
- Use o botão de atualizar.
- O cache é de cerca de 5 minutos.
- A fonte pode estar lenta; o app mostra dados antigos com aviso.
Mangás e jogos
Mangá sem capítulos
- Troque o filtro de idioma para Todos.
- Nem todo título tem tradução em português ou inglês.
- A MangaDex pode estar instável.
Páginas não carregam
- Teste outra rede.
- O leitor pré-carrega apenas as próximas páginas; role com calma.
- Se o capítulo estiver quebrado na fonte, escolha outro.
Jogo sem trailer ou screenshots
- O enriquecimento com a Steam é opcional e depende do jogo.
- Nem todo título tem mídia disponível.
Notificações
Não recebo notificações com o app fechado
- Comportamento esperado nesta versão: o Android não registra token de push. As notificações são in-app e aparecem quando o app consulta o Firebase.
O sino não aparece
- Ele só aparece para contas com acesso ativo e sessão desbloqueada.
- Se não houver mensagens não lidas, o sino pode ficar oculto.
Ajuda e site
A Central de Ajuda não abre no app
- Use Tentar novamente.
- Use Abrir no navegador.
- Confirme se https://watchverse.fun/documentacao abre no navegador.
- O app exige HTTPS; redes que bloqueiam o domínio impedem a abertura.
Desempenho, dados e bateria
- Limpe o cache do app nas configurações do Android se o armazenamento crescer.
- Use Wi-Fi para catálogo, trailers e canais.
- A reprodução em segundo plano consome bateria; feche o player quando não estiver usando.
- Em aparelhos com pouca memória, o Android pode encerrar o app ao alternar entre tarefas.
Como pedir suporte
Use https://watchverse.fun/contato.html e informe:
- e-mail da conta (sem enviar senha ou código);
- modelo do aparelho e versão do Android;
- versão do app (Perfil ou tela inicial);
- o que você tentou fazer;
- a mensagem de erro exata;
- se possível, um print da tela.
Nunca envie código de verificação, token, senha ou dados de cartão.
Desenvolvedor
Arquitetura
Visão geral
O WatchVerse é um aplicativo Expo / React Native para Android, com um Worker Cloudflare como backend. O app não hospeda mídia: ele consome catálogos públicos e abre players de terceiros.
| Camada | Tecnologia |
|---|---|
| App | Expo SDK 57, React Native 0.86.3, React 19.2.3, TypeScript |
| Navegação | React Navigation 7 (stack + bottom tabs) |
| Estado global | React Context (AuthContext, HomeBootstrapContext, RadioPlaybackContext) |
| Persistência local | AsyncStorage e SecureStore |
| Autenticação | Firebase Authentication + Google Sign-In |
| Banco em tempo real | Firebase Realtime Database |
| Backend | Cloudflare Worker (TypeScript) + D1 + R2 + Workers AI |
| Pagamento | Stripe Checkout + webhook no Worker |
| Crash/erro | Sentry React Native |
| Build | EAS Build / Gradle (assembleRelease) |
Estrutura de pastas
wv/
├── App.tsx # raiz, gate de segurança, navegação autenticada
├── index.ts # entry point do Expo
├── src/
│ ├── components/ # UI reutilizável (banner, notificações, biometria…)
│ ├── config/ # Config, NewsConfig, FootballConfig, GamesConfig, MangaLanguageConfig
│ ├── context/ # AuthContext, HomeBootstrapContext, RadioPlaybackContext
│ ├── data/ # coleções curadas (Sci-Fi Top 100, ranked collections)
│ ├── hooks/ # useRadioPlayer, useNetworkConnectivity, useRefresh
│ ├── localization/ # ptBR, enUS, dicionários completos, localeStore
│ ├── navigation/ # MainTabNavigator, navigationRef
│ ├── screens/ # 34 telas
│ ├── security/ # AppSecurityService, root/emulador/assinatura
│ ├── services/ # 34 serviços de domínio e infraestrutura
│ ├── theme/ # Theme (cores, espaçamentos)
│ ├── types/ # tipos de API, IPTV, rádio, navegação, playback
│ └── utils/ # safeUrl, mangaLabels e utilitários
├── android/ # projeto nativo gerado (manifest, gradle)
├── docs/ # esta documentação (fonte única)
├── quality-gate/ # verificações de qualidade e segurança
├── scripts/ # check-translations, generate-icons
└── __tests__/ # testes Jest
Inicialização
O fluxo real de abertura é:
index.tsregistra o componente raiz.Appexecuta as verificações de segurança (AppSecurityService) com timeout de 15 s.- Enquanto verifica, mostra a splash/indicador.
- Se o aparelho for bloqueado (root, emulador, assinatura divergente em produção), abre
SecurityBlockedScreen. - Se liberado, monta
AuthProvider,HomeBootstrapProvidereRadioPlaybackProvider. RootNavigatordecide entreLogin,VerifyCodeScreen,CheckoutScreenou o app autenticado.BiometricGateprotege a sessão quando a biometria está ativa.AuthenticatedNavigatormonta o stack principal e a tab bar.
Há um watchdog de inicialização de 20 s e um timeout de 15 s para o carregamento da autenticação, para evitar tela travada em redes ruins.
Navegação
O stack raiz (App.tsx) contém:
Main— tab navigator;Details,Player,LivePlayer;Checkout;Help— Central de Ajuda em WebView;MangaDetails,MangaReader;GameDetails,GameMedia;HomeCollection— listas “Ver tudo” (Sci-Fi Top 100 etc.);PerformanceTestseQualityGate— apenas em desenvolvimento.
As cinco abas (MainTabNavigator.tsx) têm stacks próprios:
| Aba | Stack |
|---|---|
| Início | HomeFeed, Manga, Games, LiveTv, Radio, RadioPlayer, News, NewsArticle, Football |
| Conteúdo | ContentHome, Movies, Series, Anime, Manga, Games |
| Ao Vivo | LiveHome, LiveTv, Radio, RadioPlayer, News, NewsArticle, Football |
| + | MoreHome, Search, Favorites |
| Perfil | tela única ProfileScreen |
A duplicação de telas entre stacks é intencional: cada aba mantém seu próprio histórico. navigationRef permite navegar fora de componentes (por exemplo, ao tocar em uma notificação).
Contextos
AuthContext
Concentra o estado de autenticação e acesso:
- usuário Firebase e estado de inicialização;
- status de acesso (
ACTIVE,EXPIRED,NONE,DEVICE_MISMATCH,OTP_REQUIRED,UNKNOWN,ERROR); refreshAccess()elogout();- desbloqueio por biometria (
sessionUnlocked); - registro do aparelho e sincronização de dados.
HomeBootstrapContext
Carrega em paralelo os dados da Home (tendências, populares, animes, etc.) para reduzir espera e evitar requisições repetidas. Expõe estado de carregamento e erro por bloco.
RadioPlaybackContext
Mantém o player de rádio vivo entre telas:
- estação atual e status de reprodução;
- reprodução em segundo plano e controles de tela de bloqueio;
- reconexão automática;
- espectro de áudio (quando permitido).
Serviços
Os serviços são organizados por domínio. Os principais:
| Serviço | Responsabilidade |
|---|---|
AuthService / GoogleAuthService | Login Google, logout, perfil |
AccessService | Status da licença, OTP, checkout, sessão |
SecureApiClient | Requisições autenticadas ao Worker |
TmdbService | Catálogo de filmes, séries e animes |
ProviderService | Provedores de player e montagem de embed |
IptvService / WorkerIptvService / LiveTvHealthService | Canais, cache, saúde e modo Worker |
RadioService | Estações via Worker |
NewsService | Notícias e artigos |
FootballService | Competições, tabelas e partidas |
MangaDexService / MangaProgressService | Catálogo, capítulos e progresso |
GamesService | Catálogo RAWG e detalhes |
FavoritesService | Favoritos locais e sincronizados |
PlaybackProgressService | Progresso de reprodução |
NotificationsService | Mensagens in-app e leitura |
BannerHtmlService / AdTrackingService | Banner e eventos de anúncio |
DeviceTrackingService | Registro e atualização do aparelho |
NetworkService | Conectividade e tratamento de offline |
PersistentCache | Cache com TTL em AsyncStorage |
StorageService | Chaves e leitura/gravação segura |
Armazenamento
AsyncStorage
Guarda dados não sensíveis e caches:
- favoritos locais;
- progresso de reprodução e de mangá;
- preferências da Home;
- cache de catálogo;
- avatar escolhido;
- estado de leitura das notificações;
- cache do banner.
SecureStore
Guarda dados sensíveis:
- token de sessão do Worker;
- preferência de biometria;
- estado de licença em cache;
- idioma.
O app usa allowBackup=false, então esses dados não entram em backup do Android.
Localização
src/localization/ptBR.tseenUS.tssão os dicionários base.ptBR.full.tseenUS.full.tsestendem os dicionários com chaves adicionais.LanguageContextexpõeuseT()e o idioma atual.localeStorepersiste a escolha e informa o locale ao TMDb.- O script
npm run check:translationsvalida paridade de chaves.
Tema e layout
Theme centraliza cores, espaçamentos e tipografia. O app é dark-only, com vermelho #E50914 como destaque. As telas usam SafeAreaView, grades responsivas e ajustes para tablet/DeX. A orientação padrão é livre (orientation: default), com players em paisagem imersiva.
Segurança estrutural
AppSecurityServicebloqueia root, emulador, debug e assinatura divergente em produção.expo-screen-captureimpede captura de tela.network_security_configbloqueia cleartext.- WebViews validam HTTPS e hosts permitidos.
- O Worker valida Firebase ID token, e-mail Gmail verificado, entitlement ativo e
deviceId.
Detalhes em Modelo de segurança.
Desenvolvedor
Ambiente e build
Requisitos
| Ferramenta | Versão/comentário |
|---|---|
| Node.js | 20 LTS ou superior (recomendado) |
| npm | 10 ou superior |
| JDK | 17 (exigido pelo Android Gradle Plugin atual) |
| Android SDK | API 36, build-tools compatíveis |
| Gradle | via wrapper do projeto |
| Conta Expo/EAS | necessária para build na nuvem |
| Wrangler | para publicar o Worker |
O projeto usa expo@^57, react-native@0.86.3, minSdk 29 e targetSdk 36.
Instalação
cd wv
npm ci
O postinstall executa patch-package, aplicando os patches em patches/. Não remova essa etapa.
Variáveis de ambiente
O app lê variáveis EXPO_PUBLIC_* em tempo de build. Nunca comite valores reais. As chaves esperadas são:
| Variável | Uso |
|---|---|
EXPO_PUBLIC_FIREBASE_API_KEY | Firebase |
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN | Firebase Auth |
EXPO_PUBLIC_FIREBASE_DATABASE_URL | Realtime Database |
EXPO_PUBLIC_FIREBASE_PROJECT_ID | Firebase |
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET | Storage (avatares) |
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID | Firebase |
EXPO_PUBLIC_FIREBASE_APP_ID | Firebase |
EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID | Analytics (opcional) |
EXPO_PUBLIC_WORKER_ORIGIN | Origem do Worker (padrão: https://ad-server-worker.gabrieldossantoselizeu3.workers.dev) |
EXPO_PUBLIC_ANDROID_SIGNATURE_SHA256 | SHA-256 do certificado esperado em produção |
EXPO_PUBLIC_USE_WORKER_IPTV | 1 ativa o catálogo IPTV via Worker |
EXPO_PUBLIC_ENABLE_LICENSE_GATE | Liga/desliga o gate de licença |
EXPO_PUBLIC_PURCHASE_URL | URL alternativa de compra (quando usada) |
EXPO_PUBLIC_IMAGE_BASE_URL | Base de imagens (quando configurada) |
EXPO_PUBLIC_QUALITY_GATE | 1 habilita a tela de quality gate em dev |
EXPO_PUBLIC_QUALITY_GATE_STRESS | modo de estresse do quality gate |
EXPO_PUBLIC_PERFORMANCE_TESTS | 1 habilita testes de performance em dev |
EXPO_PUBLIC_PERF_STRESS | modo de estresse de performance |
EXPO_PUBLIC_QG_FORCE_OFFLINE | força cenário offline no quality gate |
Para desenvolvimento local, crie .env a partir do seu cofre de segredos. O arquivo .env não deve ser versionado.
Credenciais nativas
google-services.json— Android/Firebase.GoogleService-Info.plist— iOS (o app não é distribuído para iOS nesta versão).@c_4_rl0s__WatchVerse.jks— keystore de release. Não versione a senha.
Scripts
| Comando | O que faz |
|---|---|
npm start | Expo dev server |
npm run android | build e execução no Android |
npm run ios | build e execução no iOS |
npm run web | Expo web (não é o alvo de produção) |
npm test | Jest |
npm run lint | ESLint |
npm run check:translations | valida chaves de tradução |
npm run quality-check | traduções + quality gate |
npm run docs:build | gera a Central de Ajuda publicada (pt + en) |
npm run docs:check | verifica se as páginas geradas estão atualizadas |
npm run build:android:apk:quality | quality check + EAS productionApk |
npm run obfuscate | ofuscação opcional de dist |
Build do APK
EAS (recomendado)
npm run build:android:apk:quality
O perfil productionApk usa:
autoIncrement: true(incrementaversionCodeno servidor EAS);:app:assembleRelease;- apenas
arm64-v8apor padrão; appVersionSource: remote.
Gradle local
cd android
./gradlew :app:assembleRelease
O APK sai em android/app/build/outputs/apk/release/.
Assinatura
O build atual usa v3 only (AGP 8.12). Como minSdk é 29, isso é aceito pelos aparelhos suportados. Se precisar de compatibilidade com verificadores antigos, pós-assine com apksigner incluindo v2:
apksigner sign --ks <keystore> --v1-signing-enabled false --v2-signing-enabled true --v3-signing-enabled true app-release.apk
A assinatura precisa corresponder ao SHA-256 configurado em EXPO_PUBLIC_ANDROID_SIGNATURE_SHA256 (padrão 19d85449…6f7f). Builds com assinatura diferente são bloqueados em produção.
Testes
npm test
npm test -- newsService.test.ts
O projeto usa Jest com jest.setup.js e mocks em __mocks__/. Há testes de serviços, segurança, biometria, player e componentes.
Estado conhecido na versão 4.4.13: a suíte principal passa, mas existem falhas pré-existentes em biometricGate.test.tsx, radioService.test.ts e iptvMemorySafety.test.ts. Elas não são causadas pela documentação nem pelo botão Ajuda.
Quality gate
quality-gate/quality-check.mjs e securityChecks.mjs verificam:
- paridade de traduções;
- configuração de segurança do Android;
- permissões declaradas;
- presença de segredos em arquivos versionados;
- alvos e configurações de build.
Rode npm run quality-check antes de publicar.
Publicação do site
O site fica em quase_dev_org/watchverse_webpage e é publicado pelo Netlify a partir do repositório watchverse_website. O APK é armazenado com Git LFS em apk/.
cd quase_dev_org/watchverse_webpage
git add .
git commit -m "docs: ..."
git push origin main
A documentação publicada é gerada a partir de wv/docs (português) e wv/docs/en (inglês) pelo script wv/scripts/build-docs.mjs (ver README). Não edite o HTML gerado à mão.
Checklist de release
- Atualizar
versioneversionCodeemapp.json. - Rodar
npm run quality-checkenpm test. - Gerar o APK e calcular o SHA-256.
- Publicar o APK e o
.sha256no site. - Gerar a documentação (
node scripts/build-docs.mjs). - Validar a página de documentação em desktop e mobile.
- Testar o botão Ajuda no APK.
- Commitar e publicar o site.
Desenvolvedor
Integrações
Mapa de integrações
| Integração | Acesso | Autenticação | Usada para |
|---|---|---|---|
| Firebase Authentication | SDK no app | Google ID token | Login Gmail |
| Firebase Realtime Database | SDK no app | Regras do Firebase | Perfil, favoritos, progresso, mensagens, banner, acesso |
| Firebase Storage | SDK no app | Regras do Firebase | Avatar do usuário |
| Worker Cloudflare | HTTPS | Firebase ID token + sessão + device | Catálogo e dados |
| TMDb | Worker | Chave no Worker | Filmes, séries, animes |
| MangaDex | app direto / proxy no site | Nenhuma | Mangás |
| RAWG | Worker | Chave no Worker | Catálogo de jogos |
| Steam Store | Worker | Nenhuma | Descrição e trailers |
| football-data.org v4 | Worker | Token no Worker | Futebol |
| freenewsapi.ai | Worker | Chave opcional | Notícias |
| Workers AI | Worker | Binding | Resumos de notícias |
| Radio Browser | Worker | Nenhuma | Rádio |
| iptv-org | app direto | Nenhuma | Listas de TV |
| Stripe | Worker | Chave secreta + webhook | Pagamento |
| Sentry | SDK no app | DSN | Erros e crashes |
| Expo Push | Worker | Token Expo | Fan-out (sem registro no Android atual) |
Nenhuma chave é distribuída no APK, exceto as variáveis EXPO_PUBLIC_FIREBASE_* (que são configuração pública do Firebase) e o SHA-256 esperado da assinatura. Chaves de TMDb, RAWG, futebol, notícias, Stripe e Firebase Admin ficam apenas no Worker.
Firebase
Authentication
- Provedor: Google.
- O app usa
@react-native-google-signin/google-signinpara obter o ID token e o Firebase para criar a sessão. - O Worker valida o ID token com a chave pública do Google e exige
email_verifiede domínio Gmail.
Realtime Database
Caminhos observados no código do app:
| Caminho | Conteúdo |
|---|---|
users/{uid} | Perfil (nome, avatar, datas) |
users/{uid}/favorites | Filmes e séries favoritos |
users/{uid}/favorites_channels | Canais de TV favoritos |
users/{uid}/playback_progress | Progresso de reprodução |
messages | Notificações in-app |
userMeta/{uid}/pushTokens | Tokens de push (não usados no Android atual) |
streaming_providers | Provedores de player |
ads/bannerHtml | HTML do banner |
O Worker também usa /access/{uid}, /entitlements/…, /payments/{sessionId}, /otp/{uid} e /devices/… para licença, OTP e aparelhos.
Storage
Usado para avatares enviados pelo usuário. A foto é recortada antes do upload.
TMDb
- Base:
https://api.themoviedb.org/3, sempre por meio do Worker. - O app envia
language(pt-BR/en-US) eregion(BR/US). - O Worker faz cache de CDN: 10 minutos para listas e 24 horas para detalhes.
- O Worker também aquece o cache das listas principais em
pt-BR/BR.
Rotas disponíveis em API do Worker.
MangaDex
- Base:
https://api.mangadex.org. - O app consulta direto (não passa pelo Worker).
- Filtros:
contentRating[]=safe,includes[]=cover_art,includes[]=author, idioma de tradução. - 24 títulos por página.
- O site usa um proxy Netlify (
/api/mangadex/*) porque a MangaDex não envia CORS.
RAWG e Steam
- Base RAWG:
https://api.rawg.io/api. - O Worker aplica limite de requisições e cache em D1.
- Ordenações: populares (
-added), recentes (últimos 120 dias) e futuros (próximos 365 dias). - 30 jogos por página (padrão), com máximo configurável no Worker.
- Quando
ENABLE_STEAM_ENRICHMENT=1, o Worker busca descrição e trailers na loja da Steam. - O idioma do detalhe é
ptouen, conformelanguage/Accept-Language.
Notícias
- Fonte:
https://freenewsapi.ai/v1/search. - O Worker consulta 50 resultados por país, janela de 48 horas, e mantém apenas o dia local do país.
- Resumos são gerados com Workers AI e limitados a 3.000 caracteres.
- A mídia só é aceita com licença reconhecida (CC BY, CC BY-SA, CC0, domínio público).
- Cron a cada 20 minutos faz o rodízio de países; limpeza de dias anteriores ocorre de hora em hora.
- O app pede 20 artigos por página.
Futebol
- Base:
https://api.football-data.org/v4. - O Worker guarda tabela, resultados e próximos jogos em D1 por cerca de 5 minutos.
?refresh=1força atualização (sujeita ao limite do provedor).- Competições:
br,it,de,es,gb,fr,pt,nl.
Rádio
- Fonte:
https://de1.api.radio-browser.info/json/stations/search. - O Worker limita a 100 estações, oculta estações quebradas e ordena por cliques.
- A rota exige acesso pago.
IPTV
Dois modos:
- Padrão (app direto): listas
iptv-org.github.iopor país, HTTPS-only, blocklist pública, cache de 24 h e verificação de saúde de 12 h. - Worker (opcional):
EXPO_PUBLIC_USE_WORKER_IPTV=1ativa/api/iptv/catalog,/accesse/streamcom URLs assinadas. Desligado por padrão.
Stripe
- Checkout hospedado, preço de US$ 1 (100 centavos) e 30 dias.
POST /checkout/sessioncria a sessão; o app abre a URL em WebView.POST /stripe/webhookvalida a assinatura, confere valor/moeda e grava o entitlement.- Não há assinatura recorrente.
Sentry
- Inicializado no app para capturar erros e crashes.
- Não envia conteúdo de vídeo, senhas ou tokens.
- O DSN é configuração de build e não deve ser tratado como segredo crítico, mas também não precisa ser divulgado na documentação do usuário.
Push
- O Worker possui fan-out via Expo Push.
- No Android atual, o app não registra token (
registerForPushNotificationsAsyncretornanull), ePOST_NOTIFICATIONSestá bloqueada. - As notificações visíveis são in-app, lidas do Realtime Database.
Limites e falhas
- Todas as APIs de terceiros podem ficar indisponíveis; o app trata erro e cache quando possível.
- O Worker aplica timeout e limite de tamanho nas respostas.
- Chaves nunca são impressas em logs; erros de RAWG e futebol são registrados sem a URL com credencial.
Referência
API do Worker
Base
https://ad-server-worker.gabrieldossantoselizeu3.workers.dev
O app usa EXPO_PUBLIC_WORKER_ORIGIN para apontar para outra origem. Todas as chamadas usam HTTPS.
Autenticação
Rotas públicas
Não exigem token:
GET /eGET /healthGET /ad/next,GET /video/:id,GET /imagem/:idPOST /trackPOST /auth/otp/send,POST /auth/otp/verify,POST /auth/session/logoutPOST /checkout/session,POST /stripe/webhook,GET /access/statusGET /checkout/success,GET /checkout/cancelPOST /api/devices/registerPOST /api/notifications/clicks
Rotas de conteúdo pago
Exigem, em conjunto:
Authorization: Bearer <Firebase ID token>válido;- e-mail Gmail verificado;
- entitlement ativo (
expiresAtno futuro); x-device-idcom 8 a 128 caracteres (A-Z,a-z,0-9,_,-) igual ao aparelho vinculado.
Falhas retornam:
| Status | error | Significado |
|---|---|---|
| 401 | unauthorized | Token ausente/inválido |
| 403 | verified_gmail_required | Conta não é Gmail verificado |
| 400 | device_required | x-device-id ausente/inválido |
| 403 | access_required | Sem entitlement ativo |
| 403 | device_mismatch | Aparelho diferente do vinculado |
| 503 | access_check_unavailable | Falha ao consultar o Firebase |
Rotas administrativas
Exigem x-admin-token (ou x-dashboard-token nas rotas de chaves):
POST /api/admin/access/revokePOST /api/notifications/fanout/runPOST /api/activation-keysGET /api/activation-keys/listGET /api/activation-keys/exportPOST /api/activation-keys/revoke
Ads e mídia
GET /ad/next
Retorna o próximo anúncio.
| Query | Efeito |
|---|---|
rn=1 | Retorna HTML pronto para WebView |
Resposta JSON:
{
"success": true,
"ad": {
"id": "…",
"type": "video",
"clickUrl": "https://…",
"src": "/video/…",
"allowSkipAfter": 5,
"muteByDefault": true
}
}
Sem anúncio: { "success": true, "ad": null }.
GET /video/:id
Serve ads/{id}/video.mp4 do R2, com suporte a Range (206).
GET /imagem/:id
Serve ads/{id}/image.{jpg,jpeg,png,webp} do R2, com cache de 1 dia.
POST /track
Registra eventos de anúncio. Limite: 60 requisições por minuto por IP.
Eventos aceitos: ad_impression, ad_click, ad_complete, banner_impression, banner_click, banner_close.
Campos comuns: event, adId, campaignId, placement, clickUrl, ts, installId, appVersion, watchedMs.
Autenticação e sessão
POST /auth/otp/send
Envia um código de 8 dígitos para o e-mail da conta. Limite de 10 requisições por minuto por IP.
Regras: validade de 5 minutos, reenvio após 60 segundos, máximo de 5 códigos por hora, 3 tentativas por código.
POST /auth/otp/verify
Valida o código e devolve um sessionToken de 64 caracteres hexadecimais. O token é guardado no SecureStore.
POST /auth/session/logout
Invalida o OTP e a sessão do usuário autenticado.
Checkout e acesso
POST /checkout/session
Cria uma sessão Stripe. Requer Firebase ID token e Gmail verificado.
Resposta:
{ "ok": true, "sessionId": "cs_…", "url": "https://checkout.stripe.com/…" }
POST /stripe/webhook
Recebe eventos do Stripe. Valida a assinatura, aceita checkout.session.completed e checkout.session.async_payment_succeeded, confere valor (100 centavos) e moeda (usd) e grava o entitlement por 30 dias. É idempotente por session.id.
GET /access/status
Requer Firebase ID token, Gmail verificado e deviceId (query ou header). Retorna status, expiração, dias restantes e chave de ativação quando houver.
Status: ACTIVE, EXPIRED, NONE, DEVICE_MISMATCH, OTP_REQUIRED.
GET /checkout/success e GET /checkout/cancel
Páginas HTML de retorno do Stripe.
Dispositivos
POST /api/devices/register
Limite: 20 por minuto por IP.
{
"deviceId": "…",
"deviceInfo": {
"os": "android",
"osVersion": "14",
"brand": "samsung",
"model": "SM-…",
"appVersion": "4.4.13"
}
}
Retorna isNew, firstSeenAt, lastSeenAt e accessCount.
Notificações
POST /api/notifications/clicks
Limite: 60 por minuto por IP.
Campos: source (push ou in_app), action (route, tmdb, action_url, dismissed), notificationId, campaignId, actionTarget, route, routeParams, tmdbId, mediaType.
POST /api/notifications/fanout/run
Executa o fan-out manualmente. Exige x-admin-token. O cron também executa o fan-out a cada minuto.
IPTV
Todas exigem acesso pago.
| Rota | Descrição |
|---|---|
GET /api/iptv/catalog/:country | Lista de canais do país |
GET /api/iptv/access/:country | URLs de acesso/assinatura |
GET /api/iptv/stream/:id | Redireciona (302) para o stream autorizado |
País com duas letras minúsculas (br, us, es, …).
Rádio
GET /api/radio/stations/:country
País com duas letras (br, us, …) ou all. Retorna até 100 estações, sem estações quebradas, ordenadas por cliques. Exige acesso pago.
Notícias
GET /api/news/:country
| Parâmetro | Padrão | Limite |
|---|---|---|
page | 1 | 1–100 |
limit | 20 | 1–50 |
format | — | summary apenas no artigo |
Países: br, us, gb, fr, de, es, it, pt, jp, cn.
Resposta inclui country, page, limit, total, hasMore, updatedAt, stale, refreshError e articles[].
GET /api/news/:country/:articleId?format=summary
Retorna o artigo completo (resumo de até 3.000 caracteres) com licenseName e licenseUrl.
Futebol
GET /api/football/:country
Países: br, it, de, es, gb, fr, pt, nl.
Query refresh=1 força atualização. Resposta inclui competição, temporada, classificação, resultados e próximos jogos, além de source e cached.
Jogos
| Rota | Parâmetros |
|---|---|
GET /api/games/popular | page, pageSize, sort (popular, recent, upcoming), genre, language |
GET /api/games/search | q (obrigatório), page, pageSize, genre, language |
GET /api/games/genres | — |
GET /api/games/:id | language |
pageSize é limitado pelo Worker; o app usa 30. Erros: invalid_page, invalid_page_size, invalid_sort, invalid_genre, invalid_query, games_upstream_failed.
TMDb
Todas exigem acesso pago. page é normalizado; language padrão pt-BR; region padrão BR.
Busca
| Rota | Query obrigatória |
|---|---|
GET /api/search/movie | query (ou q) |
GET /api/search/tv | query (ou q) |
GET /api/search/multi | query (ou q) |
Listas
| Rota | Observação |
|---|---|
GET /api/movies/popular | também aceita /api/movie/popular |
GET /api/movie/upcoming | estreias |
GET /api/tv/popular | séries populares |
GET /api/movies/trending | trending semanal de filmes |
GET /api/tv/trending | trending semanal de séries |
GET /api/trending/:mediaType/:timeWindow | movie/tv/all, day/week |
GET /api/tv/airing_today | no ar hoje |
Discover
GET /api/discover/movie:
page, region, with_genres, without_genres, primary_release_date.gte, primary_release_date.lte, sort_by, include_adult, language.
GET /api/discover/tv:
page, with_genres, with_original_language, sort_by, include_adult, language.
Detalhes
| Rota | Descrição |
|---|---|
GET /api/movie/:id | Detalhes do filme |
GET /api/movie/:id/videos | Trailers (fallback en-US) |
GET /api/movie/:id/credits | Elenco |
GET /api/tv/:id | Detalhes da série |
GET /api/tv/:id/season/:n | Temporada |
GET /api/tv/:id/credits | Elenco |
GET /api/person/:id | Pessoa (fallback de biografia em en-US) |
GET /api/person/:id/combined_credits | Créditos |
GET /api/person/:id/external_ids | IDs externos |
Rotas descontinuadas
/api/license/* e as rotas públicas de chave de ativação retornam 410 Gone:
/api/activation-keys/claim/api/activation-keys/validate-and-claim/api/activation-keys/validate
O fluxo atual é Stripe + entitlement.
Erros e CORS
- Erros seguem
{ "ok": false, "error": "código" }ou{ "error": "mensagem" }. ALLOWED_ORIGINSdefine as origens permitidas; vazio usa a política padrão do Worker.OPTIONSretorna 204 com cabeçalhos CORS.- Limites por IP usam os bindings de rate limit do Cloudflare.
Cron
wrangler.toml agenda * * * * * (a cada minuto). A cada execução:
- fan-out de notificações;
- a cada 20 minutos: aquecimento de notícias, backfill de resumos, poda do cache de jogos e aquecimento do TMDb;
- a cada hora: limpeza de notícias de dias anteriores.
Referência
Histórico de versões
4.4.13 — 03/11/2026
versionCode 19
- Corrigida a exibição de capas em PNG, WebP e GIF no catálogo de mangás; antes apenas arquivos
.jpgeram aceitos. - A lista de capítulos passou a seguir o
totaldo feed da MangaDex (com teto de 10.000), em vez de parar em 2.000. Séries longas não perdem mais capítulos. - O site passou a usar um proxy próprio para a API e para as capas, com User-Agent de servidor; a seção de mangás voltou a carregar.
- Corrigido o estouro horizontal das descrições de mangá com URLs longas no site.
- APK publicado:
watchverse_4.4.13.apk, 120.693.331 bytes. - SHA-256:
e0bf63a1495a551505e5c3071c4118f5e58d08a34a63b7ecf67e42e753465b12. - Nenhuma mudança em autenticação, licenciamento, pagamentos, mídia ou backend.
4.4.12 — 02/11/2026
versionCode 18
- Central de Ajuda publicada também em inglês, em
https://watchverse.fun/documentation. - O botão Ajuda passou a abrir a edição do idioma do app:
/documentacaopara pt-BR e/documentationpara en-US. - As duas páginas têm seletor de idioma e
hreflangrecíproco para SEO. - Nenhuma mudança em autenticação, licenciamento, pagamentos, mídia ou backend.
4.4.11 — 02/11/2026
versionCode 17
- O item Ajuda do Perfil passou a abrir esta Central de Ajuda em WebView interna (
https://watchverse.fun/documentacao?embed=1), com fallback de erro, botão de tentar novamente e opção de abrir no navegador. - A documentação passou a ser gerada a partir de
docs/(npm run docs:build) e publicada no site. - Nenhuma mudança em autenticação, licenciamento, pagamentos, mídia ou backend.
4.4.10 — 01/11/2026
versionCode 16
- Corrigido o player de rádio aberto pelos Favoritos: a rota
RadioPlayeragora é empilhada sobreLiveHomecominitial: false. O botão voltar e o toque repetido na aba Ao Vivo retornam ao hub. - APK publicado:
watchverse_4.4.10.apk, 120.680.187 bytes. - SHA-256:
c6875d1946d86d48d4ea7b08991dfcc426917c18dab7f6850e2160ec2b040b8c. - Assinatura v3 válida; certificado
19d85449…6f7f.
4.4.9 — 01/11/2026
versionCode 15
- Assinatura alterada para v1 desligada, v2 e v3 habilitadas no DSL; o AGP gerou APK v3-only, aceito por
minSdk 29. - Removidos componentes exportados sem uso:
com.google.android.gms.auth.api.signin.RevocationBoundServiceandroidx.profileinstaller.ProfileInstallReceiver
- Mantido
FirebaseInstanceIdReceiver(FCM). - Documento de supressões justificadas do MobSF criado.
- SHA-256:
2bf8f9fbc1b3ab7ab6e63dbd6d7837ed8be40a1b42e97c25eaed01dcf2d5b55d.
4.4.8 — 01/11/2026
versionCode 14
NewsServicepassou a aceitar resumos de até 3.000 caracteres, alinhado ao contrato do Worker (MAX_ARTICLE_BODY_CHARS).- Teste de regressão adicionado;
newsService.test.ts17/17. - Notícias restauradas e validadas (
GET /api/news/br200, 15 artigos).
4.4.7 — 01/11/2026
@expo/dom-webviewexcluído do autolinking por ser código morto.react-native-webviewmantido com flags perigosas desativadas e validação de HTTPS/host.- Alertas medium do MobSF documentados.
- MD5 e SHA-1 remanescentes mantidos por uso não criptográfico ou exigido por especificação.
4.4.6 — 30/10/2026
- Cleartext HTTP eliminado do build de release;
localhoste10.0.2.2movidos para a configuração de debug. - Canais IPTV sem HTTPS passaram a usar espelhos HTTPS ou foram descartados.
- Permissões de armazenamento e mídia removidas do manifesto.
allowBackup=falsee regras de backup restritas.- Whitelist do banner HTML restringida.
srcdo iframe do player escapado.- Dados locais do usuário limpos no logout.
- Rate limit por IP no fluxo de OTP.
4.4.5 — 30/10/2026
- Permissão
CAMERAremovida (não era usada). - AES-CBC com PKCS5/PKCS7 em
androidx.biometricmantido: uso apenas de criptografia, sem decifração de entrada do atacante. minSdkelevado para 29 (Android 10).
4.4.3 — 30/10/2026
minSdk 24→ 29.- WebView com depuração remota desativada em release.
Documentação — 02/11/2026
- Criada a Central de Ajuda em Markdown como fonte única.
- Publicação em
https://watchverse.fun/documentacao, com edição em inglês emhttps://watchverse.fun/documentation. - Botão Ajuda do Perfil passou a abrir a documentação em WebView, com fallback de erro.
- Adicionados capítulos de usuário, desenvolvedor, API, segurança e changelog.
- Documento interno de recomendações de segurança separado e não publicado.
Segurança
Modelo de segurança
Princípios
O WatchVerse adota quatro princípios:
- O servidor decide o acesso. O app não é a autoridade final sobre licença ou aparelho; o Worker valida token, e-mail, entitlement e
deviceId. - Privilégio mínimo. O manifesto declara apenas as permissões necessárias (internet, mídia em segundo plano, biometria, vibração).
- Transporte seguro. Todo tráfego é HTTPS; cleartext é bloqueado no release.
- Superfície pequena. Componentes exportados sem uso foram removidos; WebViews validam hosts; não há SDK de anúncio de terceiros.
Camadas
1. Integridade do aparelho e do app
Em build de produção (!__DEV__ e fora do Expo Go), AppSecurityService verifica:
| Verificação | Efeito |
|---|---|
| Ambiente de desenvolvimento / Expo Go | Bloqueia |
| Emulador | Bloqueia |
| Root (RootBeer + detecção própria) | Bloqueia |
| Assinatura Android | Compara com o SHA-256 esperado; bloqueia se divergir |
| Runtime de updates | Verifica consistência quando habilitado |
O bloqueio mostra SecurityBlockedScreen com o motivo. As verificações são executadas no início e têm timeout para não travar o app.
Limite: nenhuma verificação local é invulnerável a um atacante com controle total do aparelho. Elas elevam o custo e cobrem o usuário comum, mas a decisão de acesso continua no servidor.
2. Autenticação
- Login exclusivamente Google/Gmail.
- O Worker valida o ID token do Firebase contra as chaves públicas do Google.
- Exige
email_verifiede domínio Gmail. - A sessão do Worker usa um token de 32 bytes (64 hex) guardado no SecureStore.
- O código OTP tem 8 dígitos, validade de 5 minutos, 3 tentativas, reenvio de 60 segundos e limite de 5 por hora.
- OTP e token de sessão são armazenados como hash; a comparação é de tempo constante.
3. Autorização e licença
- O entitlement é gravado no Firebase pelo webhook do Stripe, não pelo app.
- O Worker confere valor (100 centavos), moeda (
usd) e identidade da sessão. - O webhook valida a assinatura do Stripe e é idempotente por
session.id. - A conta é vinculada a um aparelho via
deviceId; outro aparelho recebeDEVICE_MISMATCH. - Rotas de conteúdo pago exigem entitlement ativo e o mesmo
deviceId.
4. Transporte
network_security_config.xmlde release:cleartextTrafficPermitted="false", sem exceções.- O arquivo de debug tem configuração separada para
localhost/10.0.2.2e não entra no release. - Canais IPTV que só existem em HTTP usam espelhos HTTPS ou são descartados.
- Requisições do Worker têm timeout e limite de tamanho de resposta.
5. Armazenamento
| Dado | Onde | Proteção |
|---|---|---|
| Token de sessão | SecureStore | Keystore do Android |
| Preferência de biometria | SecureStore | Keystore do Android |
| Licença em cache | SecureStore | Keystore do Android |
| Idioma | SecureStore | Keystore do Android |
| Favoritos, progresso, caches | AsyncStorage | Sandbox do app |
| Backup | Desativado | allowBackup=false |
Em aparelho sem root, outros aplicativos não acessam o sandbox. O logout limpa os dados locais do usuário.
6. Biometria
- Usa
BiometricPromptcomCryptoObject. - Requer biometria forte cadastrada.
- Protege a sessão já autenticada; não substitui o login Google.
- A chave fica no Keystore e o app só a usa para montar o
CryptoObject.
O uso de AES-CBC/PKCS7 nessa biblioteca do AndroidX é apenas para criptografar com a chave do Keystore. Não há decifração de dados controlados pelo atacante, portanto não há oráculo de padding.
7. WebViews
| WebView | Conteúdo | Controles |
|---|---|---|
| Player | iframe do provedor | HTTPS, whitelist de hosts, sem bridge, src escapado |
| Checkout | Stripe | URL fixa, HTTPS |
| Banner | HTML do backend | origens restritas (about:blank, data:, blob:, HTTPS) |
| Documentação | Site oficial | HTTPS, host fixo, sem bridge |
Configurações perigosas ficam desligadas por padrão na versão usada da biblioteca:
allowFileAccess=falseallowContentAccess=falseallowFileAccessFromFileURLs=falseallowUniversalAccessFromFileURLs=falsemixedContentMode=NEVER_ALLOW- depuração remota desligada em release
Links externos passam por validação: apenas http(s) absolutos; intent:, javascript:, file: e URLs relativas a host são recusados.
8. Captura de tela
expo-screen-capture impede captura e gravação da tela enquanto o app está em uso. Isso reduz vazamento visual de conteúdo, mas não impede fotos com outro aparelho.
9. Backend
- Consultas ao D1 são parametrizadas.
- Segredos ficam em bindings do Worker, nunca no APK.
- Rate limit por IP em OTP, tracking, registro de dispositivo e cliques de notificação.
- CORS configurável por
ALLOWED_ORIGINS. - Logs não imprimem chaves; erros de provedores são registrados sem a URL com credencial.
- Rotas antigas de chave de ativação retornam 410.
O que o app não faz
- Não contorna DRM, geobloqueio ou login de provedores.
- Não baixa conteúdo protegido.
- Não coleta senha do Google (o fluxo é delegado ao Google).
- Não armazena dados de cartão (o Stripe hospeda o checkout).
- Não garante que o conteúdo de terceiros seja seguro ou esteja disponível.
Riscos residuais aceitos
| Risco | Situação | Mitigação |
|---|---|---|
| Root/emulador sofisticado | Detectores locais podem ser burlados | Servidor continua validando acesso |
| Provedores de vídeo externos | Podem coletar dados e exibir anúncios próprios | WebView sem bridge e com whitelist |
| Conteúdo de notícias de terceiros | Resumos automáticos podem errar | Link para a fonte e crédito |
| Streams IPTV instáveis | Disponibilidade varia | Saúde do stream, fallback e cache |
| Captura por câmera externa | Fora do controle do app | Aviso na política |
expo-dev-client presente no bundle | Sem launcher activity, não utilizável pelo usuário | Pendência de higiene de dependências |
| Push Android ausente | Sem token registrado | Notificações in-app |
Recomendações internas
Orientações operacionais, de hardening e de resposta a incidentes ficam em um documento interno mantido pela equipe, não publicado no site.
Reportar uma vulnerabilidade
Envie um e-mail pelo formulário em https://watchverse.fun/contato.html, descrevendo o problema e os passos para reproduzir. Não inclua dados de outras pessoas, tokens ou conteúdo de contas. O recebimento é confirmado e a correção é priorizada conforme o impacto.